home *** CD-ROM | disk | FTP | other *** search
/ HPAVC / HPAVC CD-ROM.iso / WINER.ZIP / CHAP3.TXT < prev    next >
Text File  |  1994-09-04  |  112KB  |  2,131 lines

  1.                                 CHAPTER 3
  2.  
  3.                            PROGRAMMING METHODS
  4.  
  5. In Chapters 1 and 2 you learned how the BASIC compiler translates a source
  6. file into the equivalent assembly language statements, and how it allocates
  7. memory to store variables and constants.  In particular, you saw that the
  8. BC compiler generates assembly language code directly for some statements,
  9. while for others it creates calls to routines in the BASIC libraries.  Most
  10. of the code examples presented in that chapter dealt with simple variable
  11. assignments and calculations.
  12.    Of course, the compiler must do much more than merely assign and
  13. manipulate variables and other data.  Equally important is controlling how
  14. your program operates, and determining which paths are to be taken as it
  15. progresses.  In this chapter we will delve into the inner workings of
  16. control flow structures, with an eye toward writing programs that are as
  17. efficient as possible.  As with the earlier chapters, this discussion
  18. includes numerous disassemblies of compiled BASIC code.  Thus, you will see
  19. exactly what the compiler does, and how each control flow statement is
  20. handled.
  21.    This chapter also discusses the design of both static and non-static
  22. subprograms and functions, and compares the relative merits of each method. 
  23. Many programmers do not fully understand the term Static, and find the
  24. related subject of recursive subroutines especially difficult to grasp. 
  25.    BASIC supports four types of subroutines, and each will be described in
  26. this chapter: GOSUB routines, subprograms, DEF FN functions, and what I
  27. call "formal functions".  YOu will notice that I use the terms subroutine
  28. and procedure interchangeably, to indicate a single block of code that may
  29. be executed more than once.  You will also learn how parameters are passed
  30. to these procedures.
  31.    Finally, in this chapter I will discuss programming style. Programming
  32. in any language is arguably as much of an art as it is a science.  But
  33. unlike, say, music, where a composer can write any sequence of notes and
  34. proclaim them acceptable, a computer program must at least work correctly. 
  35. There are an infinite number of ways to accomplish any programming task,
  36. and I can make recommendations only.  Which approach you choose will
  37. reflect both your own personal taste and style, as well as your current
  38. level of competence and understanding of programming in general.
  39.  
  40.  
  41. CONTROL FLOW
  42. ============
  43.  
  44. All programs--regardless of the language in which they are written--require
  45. a mechanism for testing certain conditions and then performing different
  46. actions based on those conditions.  Although there are many ways to perform
  47. tests and branches in a BASIC program, all of them do essentially the same
  48. thing.  The BASIC control flow statements are GOTO, DO/LOOP, WHILE/WEND,
  49. IF/THEN/ELSE, FOR/NEXT, SELECT CASE, ON GOTO, and ON GOSUB.  Because the
  50. capabilities of WHILE/WEND are also available with a DO/LOOP construct, the
  51. two will be discussed together.
  52.    In almost all cases, the BASIC compiler directly generates the code that
  53. controls a program's flow.  One exception is when floating point values are
  54. used as a FOR counter, or as a WHILE or UNTIL condition.  In those
  55. situations, calls are made to the floating point comparison routines in the
  56. BASIC runtime library.  Another place is when you have a statement such as
  57. CASE ASC(X$), or IF LEFT$(X$, 10) = Y$.  ASC and LEFT$ are also subroutines
  58. in the BASIC language library, and they too are invoked by calls.
  59.    It is important to reiterate that when dealing with integer test
  60. conditions, BC will in many cases create assembly language code that is as
  61. good as a human programmer would write.  In the short program fragment that
  62. follows, all of the BASIC source code is shown translated to the equivalent
  63. assembly language statements.  This listing was derived by compiling and
  64. linking the BASIC program for Microsoft CodeView, and then using CodeView
  65. to display the resultant code.
  66.  
  67.  
  68. This is what you write:
  69.  
  70. DO
  71.   X% = X% + 1
  72. LOOP WHILE X% < 100
  73.  
  74.  
  75. This is the result after compilation:
  76.  
  77. 30:
  78.   INC  WORD PTR [X%]        ;X% = X% + 1
  79.   CMP  WORD PTR [X%],64     ;compare X% to 100
  80.   JL   30                   ;jump if less to 30
  81.  
  82.  
  83. Here the variable X% is incremented, and then compared to the value 100. 
  84. (64 is the Hex equivalent to 100, which is how CodeView displays values.) 
  85. If X% is indeed less than 100, the program jumps back to address 30 and
  86. continues processing the loop.  Notice that while this example does not use
  87. a named label in the BASIC source code as the target for a GOTO, the
  88. equivalent assembly language code does.  In this case, the label is the
  89. code at address 30.  Do not confuse the addresses that assembly language
  90. must use as jump targets with the numbered labels that in BASIC are
  91. optional.
  92.  
  93.  
  94. THE DREADED GOTO
  95.  
  96. Modern programming philosophy dictates that GOTO and GOSUB statements
  97. should be avoided at all cost, in favor of DO and WHILE loops.  However,
  98. all of these methods result in nearly identical code.  Indeed, there is
  99. nothing inherently wrong with using GOTO when circumstances warrant it. 
  100. By examining the program listing below, you will see that BASIC generates
  101. code that is identical for a GOTO as for a DO loop.
  102.  
  103.  
  104. This is what you write:
  105.  
  106. Label:
  107.   X% = X% + 1
  108.   IF X% < 100 THEN GOTO Label
  109.  
  110.  
  111. This is the result after compilation:
  112.  
  113. 30:
  114.   INC  WORD PTR [X%]        ;X% = X% + 1
  115.   CMP  WORD PTR [X%],64     ;compare X% to 100
  116.   JL   30                   ;jump if less to 30
  117.  
  118.  
  119. Since GOTO and DO/LOOP produce the same results, which one is better, and
  120. why?  In general, a DO/LOOP is preferable for two reasons.  First, it is
  121. a nuisance to have to create a new and unique label name for every location
  122. that a program may need to branch to.  Admittedly, in a short program this
  123. will not be a problem.  But in a large application with many small loops
  124. that test for keyboard input, you end up creating many labels with names
  125. such as GetKey1, GetKey2, and so forth.  And if you inadvertently use the
  126. wrong label name, your program will not work correctly.
  127.    More important, however, is that for each label you define in a program,
  128. the BC compiler must remember its name and the equivalent address in the
  129. object code that the label identifies.  Since label names can be as long
  130. as 40 characters and memory addresses require 2 bytes each to identify, a
  131. finite number of label names can be accommodated.  By avoiding unnecessary
  132. labels, you are giving BC that much more memory to use for compiling your
  133. program.
  134.    There are several situations in which GOTO is preferable to a DO or
  135. WHILE loop.  Indeed, one of my personal pet peeves is when a programmer
  136. tries to shoehorn structure into a program no matter what the cost. 
  137. Consider the three different code fragments below; each waits for a key
  138. press and then assigns it to the variable Ky$.
  139.  
  140.  
  141. This approach is the worst:
  142.  
  143. Ky$ = ""
  144. WHILE Ky$ = ""
  145.   Ky$ = INKEY$
  146. WEND
  147.  
  148.  
  149. This method is better:
  150.  
  151. Label:
  152.   Ky$ = INKEY$
  153.   IF Ky$ = "" GOTO Label
  154.  
  155.  
  156. And this is better still:
  157.  
  158. DO
  159.   Ky$ = INKEY$
  160. LOOP WHILE Ky$ = ""
  161.  
  162.  
  163. In the first example, an extra step is needed solely to clear Ky$ to a null
  164. string, so the initial WHILE will be true and execute at least once.  Every
  165. string assignment adds 13 bytes to a program, and those 13 bytes can add
  166. up quickly in a large application.
  167.    The second example avoids the unnecessary assignment, but adds a label
  168. for GOTO to jump to.  Although this label does require a small amount of
  169. additional memory while the program is being compiled, it does not increase
  170. the size of the final executable program file.
  171.    The last example is better still, because it avoids the need for a line
  172. label and also avoids an extra string assignment.  Since a DO loop allows
  173. the test to be placed at either the top or bottom of the loop, you can
  174. force the loop to be executed at least once by putting the test at the
  175. bottom as shown here.
  176.    However, even this can be improved upon by eliminating the string
  177. comparison that checks if Ky$ is equal to a null string.  If we replace
  178. LOOP WHILE Ky$ = "" with LOOP UNTIL LEN(Ky$), only 13 bytes of code are
  179. generated instead of 15.  When two strings are compared (Ky$ and ""), each
  180. must be passed to the string comparison routine.  Since LEN requires only
  181. one argument, the code to pass the second parameter is avoided.
  182.    There are some situations for which the GOTO is ideally suited.  In the
  183. first two examples below, a complex expression is used as the condition for
  184. executing a DO WHILE loop, and the same expression is then used again
  185. within the loop.
  186.  
  187.  
  188. DO WHILE (X% + Y%) * Z% > 13
  189.   IF (X% + Y%) * Z% = 100 THEN PRINT
  190.   ...
  191.   ...
  192. LOOP
  193.  
  194.  
  195. DO WHILE ASC(MID$(S$, A%, B%)) > 13
  196.   IF ASC(MID$(S$, A%, B%)) > 100 THEN PRINT
  197.   ...
  198.   ...
  199. LOOP
  200.  
  201.  
  202. Label:
  203.   Temp% = ASC(MID$(S$, A%, B%))
  204.   IF Temp% > 13 THEN
  205.     IF Temp% > 100 THEN PRINT
  206.     ...
  207.     ...
  208.   GOTO Label
  209.   END IF
  210.  
  211.  
  212. In the first example, BASIC remembers the results of its test that checks
  213. if a (X% + Y%) * Z% is greater than 13, and it uses the result it just
  214. calculated in the next test that compares the same expression to 100.  This
  215. is one more example of the kinds of optimizations BC performs as it
  216. compiles your programs.  String expressions such as those used in the
  217. second example are of necessity more complex, and require calls to library
  218. routines.  With this added complexity, BASIC unfortunately cannot retain
  219. the result of the earlier comparison, and it generates identical code a
  220. second time.
  221.    A more elegant solution in this case is therefore the GOTO as shown in
  222. the last example.  Because the result of evaluating the expression is saved
  223. manually, it may be reused within the loop.  As proof, the second DO WHILE
  224. example above requires 73 bytes to implement, as opposed to only 53 when
  225. Temp% and GOTO are used.
  226.    I should also point out that the most common and valuable use for GOTO
  227. is to get out of a deeply nested series of IF or other blocks of code.  It
  228. is not uncommon to have a FOR/NEXT loop that contains a SELECT CASE block,
  229. and within that a series of IF/ELSE tests.  The only way to jump out of all
  230. three levels at once is with a GOTO.
  231.  
  232.  
  233. FOR/NEXT LOOPS
  234.  
  235. Unlike WHILE and DO loops that can test for nearly any condition and at
  236. either the top or bottom of the loop, a FOR/NEXT loop is intended to
  237. perform a block of statements a fixed number of times.  A FOR/NEXT loop
  238. could also be replaced with code that compares a value and uses GOTO to
  239. reenter the loop if needed, but that is hardly necessary.  My point is to
  240. yet again illustrate that all of BASIC's seemingly fancy constructs are no
  241. more than tests and GOTOs deep down at the assembly language level.
  242.    A FOR/NEXT loop determines the number of iterations that will be
  243. executed once ahead of time, before the loop begins.  For example, the
  244. listing below shows a loop that changes the upper limit inside the loop. 
  245. However the loop still executes 10 times.
  246.  
  247.  
  248. Limit% = 10
  249. FOR X% = 1 TO Limit%
  250.   Limit% = 5
  251.   PRINT Limit%
  252. NEXT
  253.  
  254.  
  255. The code that BASIC produces for the FOR/NEXT loop in the previous example
  256. is translated to the following equivalent during the compilation process.
  257.  
  258.  
  259.   Limit% = 10
  260.   Temp% = Limit%
  261.   X% = 1
  262.   GOTO Next:
  263. For:
  264.   Limit% = 5
  265.   PRINT Limit%
  266.   X% = X% + 1
  267. Next:
  268.   IF X% <= Temp% THEN GOTO For
  269.  
  270.  
  271. Please understand that changing a loop condition inside the loop is
  272. considered bad practice, because the program becomes difficult to
  273. understand.  If you really need to alter the limit inside a loop, the loop
  274. should be recoded to use WHILE or DO instead.  Another good reason for
  275. avoiding such code is because it is possible that future versions of BASIC
  276. will behave differently than the one you are using now.  If Microsoft were
  277. to modify BASIC such that the limit condition were reevaluated at the NEXT
  278. statement, your code would no longer work.  It is also considered bad
  279. practice to modify the loop counter variable itself (X% in the previous
  280. examples).  However, this causes no real harm, and you should not be afraid
  281. to do that if the situation warrants it.  Of course, changing the loop
  282. counter will affect the number of times the loop is executed.
  283.  
  284.  
  285. IF/THEN/ELSE AND SELECT CASE
  286.  
  287. BASIC provides two methods for testing conditions in a program, and
  288. executing different blocks of code based on the result.  The most common
  289. method is the IF test, which can be used on a single variable, the result
  290. of an expression, the returned value from a function, or any combination
  291. of these.  I won't belabor the most common uses for IF here, but I do want
  292. to point out some of its less obvious properties.  Also, there are some
  293. situations where IF and ELSEIF are appropriate, and others where their
  294. counterpart, SELECT CASE, is better.
  295.    As you have already learned, a simple IF test will in most cases be
  296. translated into the equivalent assembler instructions directly.  In some
  297. cases, however, the condition you specify is tested, while in others the
  298. *opposite* condition is tested.  If you say IF X > 10 THEN GOTO Label,
  299. BASIC may change that to IF X <= 10 GOTO [next statement].  Which BASIC
  300. uses depends on what you will do if the condition is true, and how far away
  301. in the generated code the statements that will be executed are located. 
  302. When a GOTO is to be performed if the test passes, then the relative
  303. position of the target label is also a factor.
  304.    A jump to a location either ahead in the code or more than 128 bytes
  305. backwards requires BASIC to generate more code.  The 128 byte displacement
  306. is significant, because the 80x86 can perform a *conditional jump* to an
  307. address only a limited distance away.  That is, after a comparison is made,
  308. the target address for a conditional jump such as "Jump if Greater" must
  309. be no more than that many bytes distant.  However, an unconditional jump
  310. can be to any address within the same 64K code segment.  (Bear with me for
  311. a moment, because the significance of this will soon become apparent.) 
  312. This is shown in the next listing following.
  313.  
  314.  
  315. IF X% = 100 THEN
  316.   CMP  Word Ptr [X%],64   ;compare X% to 100
  317.   JE   003A               ;jump ahead if equal
  318.   JMP  Label              ;else, skip ahead
  319. 003A:                     ;BASIC made this label
  320. Y% = 2
  321.   MOV  Word Ptr [Y%],2
  322. END IF
  323.  
  324. Label:
  325. IF X > 8 GOTO Label
  326.   CMP  Word Ptr [X%],8    ;compare X% to 8
  327.   JG   Label              ;jump back if greater
  328.  
  329.  
  330. In the first example above, BASIC compares the value of X% to 100 (64 Hex),
  331. and if equal jumps ahead to a label it created at address 003A Hex. 
  332. Otherwise, a jump is made to the next statement in the program, which in
  333. this case is a named label.  Although using two jumps may seem
  334. unnecessarily convoluted, it is necessary because BASIC has no way of
  335. knowing how many statements will follow at the time it compiles the IF
  336. test.  Thus, it also cannot know whether the statement following the END
  337. IF will end up being 128 or more bytes ahead.
  338.    By jumping to another, unconditional jump, BC is assured that the
  339. generated code will be legal.  (When BC finally encounters the END IF, it
  340. goes back to the code it created earlier, and completes the portion of the
  341. unconditional jump instruction that tells how far to go.)  Some compilers
  342. avoid this situation and create the longer, two-jump code on a trial basis,
  343. but then go back and change it to the shorter form if possible.  These are
  344. called two-pass compilers, because they process your source code in two
  345. phases.  Unfortunately, current versions of Microsoft BASIC do not use more
  346. than one pass.
  347.    In the second example Label has already been encountered, and BC knows
  348. that the label is within 128 bytes.  Therefore, it can translate the IF
  349. statement directly, without having to conditionally jump to yet another
  350. jump.  Had the earlier label been farther away, though, an extra jump would
  351. have been needed.  It is important to understand that forward jumps are
  352. always handled with more code than is likely necessary, because BASIC does
  353. not know how far ahead the jump must go.  In fact, this same issue must be
  354. dealt with when writing in assembly language, since the conditional jump
  355. distance limitation is inherent in the 80x86 microprocessor.
  356.    The bottom line, therefore, is that you can in many cases reduce the
  357. size of your programs by controlling in which direction a conditional jump
  358. will be performed.  For example, almost all programs must at some point sit
  359. in a loop waiting until a key is pressed.  The next listing shows two
  360. common ways to do this, with one testing for a key press at the top of the
  361. loop, and the other doing the test at the bottom.
  362.  
  363.  
  364. DO UNTIL LEN(INKEY$)    ;this comprises 18 bytes
  365. 0030:
  366.   CALL B$INKY           ;call INKEY$
  367.   PUSH AX               ;pass the result to LEN
  368.   CALL B$FLEN           ;AX now holds the length
  369.   AND  AX,AX            ;see if it's zero
  370.   JZ   0042             ;yes, jump to LOOP
  371.   JMP  0044             ;no, jump out of loop
  372. 0042:
  373. LOOP
  374.   JMP  0030             ;jump back to DO
  375.  
  376.  
  377. 0044:
  378. DO                      ;this is only 15 bytes
  379. LOOP UNTIL LEN(INKEY$)
  380.   CALL      B$INKY      ;call INKEY$
  381.   PUSH      AX          ;as above
  382.   CALL      B$FLEN
  383.   AND       AX,AX
  384.   JZ        0044        ;jump back if zero
  385.  
  386.  
  387. Viewed from a purely BASIC perspective, these two examples operate
  388. identically.  But as you can see, the code that BASIC creates is more
  389. efficient for the second example.  When BASIC encounters the first DO
  390. statement, it has no idea how many more statements there will be until the
  391. terminating LOOP.  Therefore, it has no recourse but to create an extra
  392. jump.  In the second example, the location of the DO is already known to
  393. be within 128 bytes, so the LOOP test can branch back using the shorter and
  394. more direct method.
  395.    An ELSEIF statement block is handled in a similar fashion, with code
  396. that directly compares each condition and branches accordingly.  Because
  397. the code to be executed if the IF is true is always after the IF test
  398. itself, the less efficient two-jump code must be generated.  A simple
  399. IF/ELSEIF follows, shown as a mix of BASIC and assembly language
  400. statements.
  401.  
  402.  
  403. IF X% > 9 THEN
  404.   CMP  Word Ptr [X%],9  ;compare X% to 9
  405.   JG   003A             ;assign Y% if greater
  406.   JMP  0043             ;else jump to next test
  407. 003A:
  408. Y% = 1
  409.   MOV  Word Ptr [Y%],1  ;assign Y%
  410.   JMP  0066             ;jump out of the block
  411. ELSEIF X% > 5 THEN
  412. 0043:
  413.   CMP  Word Ptr [X%],5  ;as above
  414.   JG   004D
  415.   JMP  0066
  416. 004D:
  417. Y% = 2
  418.   MOV  Word Ptr [Y%],2
  419. END IF
  420. 0066:
  421.   ...
  422.   ...
  423.  
  424.  
  425. Aside from the additional jumping over jumps that are added to all forward
  426. address references, this code is translated quite efficiently.  In this
  427. situation, the compiled output is identical to that produced had SELECT
  428. CASE been used.  However, there is one important situation in which SELECT
  429. CASE is more efficient than IF and ELSEIF.
  430.    For each ELSEIF test condition, code is generated to create a separate
  431. comparison.  When a simple comparison such as X% > 9 is being made, only
  432. one assembly language statement is needed.  But when an expression is
  433. tested--for example, ABS((X% + Y%) * Z%)) > 9--identical code is generated
  434. repeatedly.  This is illustrated in the listing that follows.
  435.  
  436.  
  437. IF ABS((X% + Y%) * Z%) = 5 THEN
  438.   A% = 1
  439. ELSEIF ABS((X% + Y%) * Z%) = 6 THEN
  440.   A% = 2
  441. ELSEIF ABS((X% + Y%) * Z%) = 7 THEN
  442.   A% = 3
  443. END IF
  444.  
  445.  
  446. Each time BC encounters the expression ABS((X% + Y%) * Z%), it duplicates
  447. the same assembly language statements.  But when SELECT CASE is used, the
  448. expression is evaluated once, and used for each subsequent test.  The first
  449. example in the next listing shows how SELECT CASE could be used to provide
  450. the same functionality as the preceding IF/ELSEIF block, but with much less
  451. code.  The second example then shows what SELECT CASE really does, using
  452. an IF/ELSEIF equivalent.
  453.  
  454.  
  455. You write it this way:
  456.  
  457. SELECT CASE ABS((X% + Y%) * Z%)
  458.   CASE 5: A% = 1
  459.   CASE 6: A% = 2
  460.   CASE 7: A% = 3
  461.   CASE ELSE
  462. END SELECT
  463.  
  464.  
  465. BASIC really does this:
  466.  
  467. Temp% = ABS((X% + Y%) * Z%)
  468. IF Temp% = 5 THEN
  469.   A% = 1
  470. ELSEIF Temp% = 6 THEN
  471.   A% = 2
  472. ELSEIF Temp% = 7
  473.   A% = 3
  474. END IF
  475.  
  476.  
  477. As you can see, SELECT CASE evaluates the expression once, stores the
  478. result in a temporary variable, and then uses that variable repeatedly for
  479. all subsequent comparisons.  Therefore, when the same expression is to be
  480. tested multiple times, SELECT CASE will be more efficient than IF and
  481. ELSEIF.  This is also true for string expressions and other functions.  For
  482. example, SELECT CASE LEFT$(Work$, 10) will result in less code and faster
  483. performance than using IF and ELSEIF with that same expression more than
  484. once.
  485.    Another important feature of SELECT CASE is its ability to use either
  486. variable or constant test conditions, and to operate on a range of values. 
  487. For example, the C language Switch statement which is the equivalent of
  488. BASIC's SELECT CASE can use only constant numbers for each test.  BASIC is
  489. particularly powerful in this regard, and allows any legal expression for
  490. each CASE condition.  For example, CASE IS > (Y AND Z) is valid, and so is
  491. CASE 0 TO Max.  CASE also accepts multiple conditions separated by commas
  492. such as CASE 1, 3, 4 TO 100, -10 TO -1.  In this case, the statements that
  493. follow will be executed if the selected expression equals 1, 3, any value
  494. between 4 and 100 inclusive, or any value between -10 and -1 inclusive.
  495.    It is also worth mentioning here that QuickBASIC version 4.0 contains
  496. an interesting and irritating quirk that requires a CASE ELSE in the event
  497. that none of the tests match.  Had the CASE ELSE been omitted from the
  498. previous example and the value of the expression was not between 5 and 7,
  499. QuickBASIC 4.0 would issue a "CASE ELSE expected" error at run time. 
  500. Fortunately, this has been repaired in QuickBASIC 4.5 and later versions.
  501.    Notice that this is not a bug in QuickBASIC.  Rather, it is the behavior
  502. described in the ANSI (American National Standards Institute) specification
  503. for BASIC.  At the time QuickBASIC 4.0 was introduced, Microsoft mistakenly
  504. believed the then-proposed ANSI standard for BASIC would be significant. 
  505. As that standard approached fruition, it became clear to Microsoft that the
  506. only standard most programmers really cared about was Microsoft's.
  507.    One final point I cannot make often enough is the inherent efficiency
  508. of integer operations and comparisons.  This is especially true in the
  509. comparisons that are made in both IF and CASE tests.  In the first example
  510. below, each of the characters in a string is tested in turn.  The second
  511. example shows a much better way to write such a test, by obtaining the
  512. ASCII value once and using that for subsequent integer comparisons.
  513.  
  514.  
  515. Not recommended:
  516.  
  517. FOR X = 1 TO LEN(Work$)
  518.   SELECT CASE MID$(Work$, X, 1)
  519.     CASE CHR$(9): PRINT "Tab key"
  520.     CASE CHR$(13): PRINT "Enter key"
  521.     CASE CHR$(27): PRINT "Escape key"
  522.     CASE "A" TO "Z", "a" TO "z": PRINT "Letter"
  523.     CASE "0" TO "9": PRINT "Number"
  524.   END SELECT
  525. NEXT
  526.  
  527.  
  528. Much more efficient:
  529.  
  530. FOR X = 1 TO LEN(Work$)
  531.   SELECT CASE ASC(MID$(Work$, X, 1))
  532.     CASE 9: PRINT "Tab key"
  533.     CASE 13: PRINT "Enter key"
  534.     CASE 27: PRINT "Escape key"
  535.     CASE 65 TO 90, 97 TO 122: PRINT "Letter"
  536.     CASE 48 TO 57: PRINT "Number"
  537.   END SELECT
  538. NEXT
  539.  
  540.  
  541. In the first program the SELECT itself generates 27 bytes, which is
  542. comprised of a call to the MID$ function and then a call to the string
  543. assign routine.  A string assignment is needed to save the MID$ result in
  544. a temporary variable for the subsequent tests that follow.  Each CASE test
  545. that uses CHR$ adds 27 bytes, and this includes the call to CHR$ as well
  546. as an additional call to the string comparison routine.  Testing for the
  547. letters adds 75 bytes, and testing for the numbers adds 39 more.  This
  548. results in a total code size of 222 bytes, not counting the FOR/NEXT loop.
  549.    Contrast that with only 131 bytes for the second example, in which the
  550. SELECT portion requires only 26 bytes.  Although an extra call is needed
  551. to obtain the ASCII value of the extracted character, the lack of a
  552. subsequent string assignment more than makes up for that.  Further, the
  553. tests for 9, 13, and 27 require only 13 bytes each, compared to 27 when
  554. CHR$ values were used.  The letters test requires 43 bytes, and the numbers
  555. test only 23.
  556.    Clearly this is a significant improvement, especially in light of the
  557. small number of tests that are being performed here.  In a real program
  558. that performs hundreds of string comparisons, replacing those with integer
  559. comparisons where appropriate will yield a substantial size reduction.
  560.  
  561.  
  562. AND, OR, EQV, and XOR
  563.  
  564. When you use AND or OR in an IF test, what is really being compared is
  565. either 0 or -1.  That is, BASIC evaluates the *truth* of each expression
  566. being tested on both sides of the AND or OR, and a truth in BASIC always
  567. results in one or the other of these values.  Once each expression has been
  568. evaluated, the results are combined using an assembly language AND or OR
  569. instruction, and a branch is then made accordingly.  Remember that when
  570. integers are treated as unsigned, setting all of the bits to 1 results in
  571. a value of -1.
  572.    In chapter 2 I showed how the various logical operators are used to
  573. manipulate bits in an integer or long integer variable.  The concept is
  574. identical when these operators are used for decision-making in a BASIC
  575. program.  The difference is really more a matter of semantics than
  576. definition.  That is, the same bit manipulation is performed, only in this
  577. case on the result of the truth of a BASIC expression.  This is shown in
  578. context below, where two test expressions are combined using AND.
  579.  
  580.  
  581. IF X > 1 AND Y < 2 THEN
  582.   CMP  Word Ptr [X%],1   ;compare X% to 1
  583.   MOV  AX,0              ;assume False
  584.   JLE  003B              ;we assumed correctly
  585.   DEC  AX                ;wrong, decrement to -1
  586. 003B:
  587.   CMP  Word Ptr [Y%],2   ;now compare Y% to 2
  588.   MOV  CX,0000           ;assume False
  589.   JGE  0046              ;we assumed correctly
  590.   DEC  CX                ;wrong, decrement to -1
  591. 0046:
  592.   AND  CX,AX             ;combine the results
  593.   AND  CX,CX             ;(this is redundant)
  594.   JNZ  004F              ;if not 0 assign Z%
  595.   JMP  0055              ;else jump past END IF
  596. Z = 3
  597. 004F:
  598.   MOV  Word Ptr [Z%],3   ;assign Z%
  599. END IF
  600. 0055:
  601.   ...
  602.   ...
  603.  
  604.  
  605. The result of the first comparison is saved in the AX register as either
  606. 0 or -1, and the second is saved in CX using similar code.  Once both tests
  607. have been performed and AX and CX are holding the appropriate values, the
  608. registers are then tested against each other using AND.  The instruction
  609. AND CX,AX not only combines the results, but it also sets the CPU's Zero
  610. Flag to indicate if the result was zero or not.  Therefore, the second test
  611. that uses AND to compare CX against itself to check for a zero result is
  612. redundant.  At only 2 additional bytes, the impact on a program's size is
  613. not terribly significant.  However, this shows first-hand the difference
  614. between code written by a compiler and code written by a person.
  615.    OR conditions are handled similarly, except the assembly language OR
  616. instruction is used instead of AND.  When multiple conditions are being
  617. tested using combinations of AND and OR and perhaps nested parentheses as
  618. well, additional similar code is employed.
  619.    There are many situations where all that is really necessary is to test
  620. for a zero or non-zero condition.  For example, it is common to use an
  621. integer variable as a True/False "flag" which can be set in one part of a
  622. program, and tested in another.  By understanding the underlying code that
  623. BASIC creates, you can help BASIC to reduce the size of your programs
  624. enormously.  In particular, avoiding a comparison with an explicit value
  625. lets BASIC generate fewer comparison instructions.  The listing below shows
  626. how you can test multiple flags using AND, but with much less resulting
  627. code than using an explicit comparison.
  628.  
  629.  
  630. IF Flag1% AND Flag2% THEN
  631.   MOV  AX,[Flag2%]       ;move Flag2% into AX
  632.   AND  AX,[Flag1%]       ;AND that with Flag1%
  633.   AND  AX,AX             ;(this is redundant)
  634.   JNZ  0063              ;if not zero assign Z%
  635.   JMP  0069              ;else skip past END IF
  636. Z% = 3
  637. 0063:
  638.   MOV  Word Ptr [Z%],3
  639. END IF
  640. 0069:
  641.   ...
  642.   ...
  643.  
  644.  
  645. The key here is that zero is always used to represent False, and -1 to
  646. represent a True condition.  That is, instead of writing IF Flag1% = -1 AND
  647. Flag2% = -1, using IF Flag1% AND Flag2% provides the same results.  At only
  648. 20 bytes of generated code, this method is far superior to tests for an
  649. explicit -1 which require 37 bytes.  If you recall, in Chapter 2 I showed
  650. how the various bits in a variable can be turned on or off with AND.  Thus,
  651. 1111 AND 1111 equals 1111, while 1111 AND 0000 equals 0.
  652.    Notice that using 0 and -1 has many other benefits as well.  For
  653. example, the NOT operator which was also described in Chapter 2 can toggle
  654. a variable between those values.  If all of the bits in a variable are
  655. presently zero, then NOT Variable% results in all ones (-1).  This property
  656. can also be used to enhance a program's readability, by using NOT much like
  657. you would in an English sentence.  For example, the code following the line
  658. IF NOT Flag% THEN will be executed if Flag% is 0 (False), but it will not
  659. be executed if Flag% is -1 (True).
  660.    In fact, an explicit comparison is optional if you need to test only for
  661. a non-zero value.  IF Variable <> 0 THEN can be reduced to IF Variable
  662. THEN, and the statements that follow will be executed as long as Variable
  663. is not 0.  Notice that the only saving here is in the BASIC source, since
  664. either comparison creates ten bytes of assembler code.  But when using long
  665. integers, the short form saves five bytes--14 bytes versus 19 for an
  666. explicit comparison to zero.
  667.    NOT is equally valuable when toggling a flag variable between two
  668. values.  If you have, say, an input routine that keeps track of the Insert
  669. key status, then you could use Insert% = NOT Insert% each time you detect
  670. that the Insert key was pressed.  The first time the operator presses that
  671. Key, the Insert flag will be switched from the default start-up value of
  672. 0 to -1.  Then using Insert% = NOT Insert% a second time will revert the
  673. bits back to all zeros.  In fact, it is a common technique to define True
  674. and False variables (or constants) in a program using this:
  675.  
  676.    False% = 0
  677.    True% = NOT False%
  678.  
  679. Most programmers understand how to use parentheses to force a particular
  680. order of evaluation.  By default, BASIC performs multiplication and
  681. division before it does addition and substraction.  When operators of the
  682. same precedence are being used, then BASIC simply works from left to right. 
  683. However, the order in which logical comparisons are made is not always
  684. obvious.  This can become particularly tricky if you are using some of the
  685. shorthand methods I described earlier.
  686.    For example, consider the statements IF X AND Y > 12, IF NOT X OR Y, and
  687. IF X AND Y OR Z.  In the first example, the truth of the expression Y > 12
  688. is evaluated first, with a result of either 0 or -1.  Then, that result is
  689. combined logically with the value of X using AND.  The resulting order of
  690. evaluation is performed as if you had used IF X AND (Y > 12).  The other
  691. expressions are evaluated as IF (NOT X) OR Y and IF (X AND Y) OR Z.
  692.    The last logical operators we will consider are EQV and XOR. These are
  693. used rarely by most BASIC programmers, probably because they are not well
  694. understood.  However, EQV can dramatically reduce the size of a program in
  695. certain circumstances.  It is not uncommon to test if two conditions are
  696. the same, whether True or False.  EQV stands for Equivalent, meaning it
  697. tests if the expressions are the same--either both true or both false.  All
  698. three program fragments below serve the same purpose, however the first
  699. generates 57 bytes, while the second and third create only 16 bytes.
  700.  
  701.  
  702. IF (X = -1 AND Y = -1) OR (X = 0 AND Y = 0) THEN
  703.   ...
  704. END IF
  705.  
  706. IF X EQV Y THEN
  707.   ...
  708. END IF
  709.  
  710. IF NOT (X XOR Y) THEN
  711.   ...
  712. END IF
  713.  
  714.  
  715. Although these examples could be replaced with a simple comparison that
  716. tests if X equals Y, EQV can reduce other, more elaborate AND and OR tests. 
  717. For example, you could replace this:
  718.  
  719.    IF (X = 10 AND Y = 100) OR (X <> 10 AND Y <> 100)
  720.  
  721. with this:
  722.  
  723.    IF X = 10 EQV Y = 100
  724.  
  725. and gain a handsome reduction in code size.  Notice that because of the way
  726. EQV works, the third example in the listing above results in identical
  727. assembly language code as the second.  XOR is true only when the two
  728. conditions are different, thus NOT XOR is true when they are the same.
  729.    One final point worth mentioning is that you can assign a variable based
  730. on the truth of one or more expressions.  As you saw earlier, every IF test
  731. that is used in a BASIC program adds a minimum of 3 extra bytes for a
  732. second, unconditional jump.  That additional code can be avoided in many
  733. cases by assigning a variable based on whether a particular condition is
  734. true or not.  In the code examples that follow, both program fragments do
  735. the same thing, except the first requires 25 bytes compared to only 14 for
  736. the second.
  737.  
  738.  
  739. IF Variable = 20 THEN
  740.   Flag = -1
  741. ELSE
  742.   Flag = 0
  743. END IF
  744.  
  745.  
  746. Flag = (Variable = 20)
  747.  
  748.  
  749. In either case, the truth of the expression Variable = 20 must be
  750. evaluated.  However, the IF method adds code to jump around to different
  751. addresses that assign either -1 or 0 to Flag.  The second example simply
  752. assigns Flag directly from the 0 or -1 result of the truth test.  Other
  753. variants on this type of programming are statements such as A = (B = C),
  754. and Flag = (LEN(Temp$) <> 0 AND Variable < 50).  Note that the surrounding
  755. parentheses are shown here for clarity only, and BASIC produces the same
  756. results without them.
  757.  
  758.  
  759. Short Circuits
  760.  
  761. There is one important point regarding AND testing you should be aware of. 
  762. Although the code that BASIC creates to implement these logical tests is
  763. very efficient, in some cases a different approach can yield even better
  764. results.  When many conditions are tested, QuickBASIC creates assembly
  765. language code to evaluate all of them before making a decision.  This can
  766. be wasteful, because often one of the conditions will be false, negating
  767. a need to test the remaining conditions.  For example, this statement:
  768.  
  769.    IF Any$ = "Quit" AND IntVar% > 100 AND Float! <> 0 THEN PRINT "True"
  770.  
  771. requires that all three conditions be tested before the program can
  772. proceed.  But if Any$ is not equal to "Quit", there is no need reason to
  773. spend time evaluating the other tests.
  774.    The solution is to instead use nested IF tests, preferably placing the
  775. most likely (or simplest) tests first, as shown below.
  776.  
  777.  
  778. IF Any$ = "Quit" THEN
  779.   IF IntVar% > 100 THEN
  780.     IF Float! <> 0 THEN
  781.       PRINT "True"
  782.     END IF
  783.   END IF
  784. END IF
  785.  
  786.  
  787. Here, if the first test fails, no additional time is wasted testing the
  788. remaining conditions.  Further, using the nested IF tests with QuickBASIC
  789. also results in less code: 50 bytes versus 64.  Note, however, that BASIC
  790. PDS [and VB/DOS] incorporate a technique known as *short circuit expression
  791. evaluation*, which generates slightly more efficient code when AND is used. 
  792. With the newer compilers, each condition is tested in sequence, and the
  793. first one that fails causes the program to skip over the code that prints
  794. "True".  But even with this improved code generation, you should still
  795. place the most likely tests first.
  796.  
  797.  
  798. ON GOTO AND ON GOSUB STATEMENTS
  799.  
  800. The last non-procedural control flow statements I will discuss here--ON
  801. GOTO and ON GOSUB--are used infrequently by many BASIC programmers.  But
  802. when you need to test many different values *and* those values are
  803. sequential, ON GOTO and ON GOSUB can reduce substantially the amount of
  804. code that BASIC generates.  For clarity, I will use ON GOTO for most of the
  805. examples that follow.  Both work in a similar fashion except with ON GOSUB,
  806. execution resumes at the next BASIC statement when the subroutine returns.
  807.    You have already seen that IF/ELSEIF and SELECT CASE blocks are not as
  808. efficient as they could be, because the compiler does not know how far
  809. ahead the END IF or END SELECT statements are located.  Therefore, no
  810. matter how trivial the IF or CASE tests being performed are, a pair of
  811. jumps is always created even when a single jump would be sufficient. 
  812. Further, when many tests are necessary, there is no avoiding at least some
  813. amount of code for each comparison.  This is where ON GOTO can help.
  814.    Rather than perform a series of separate tests for each value being
  815. compared, ON GOTO uses a lookup table which is imbedded in the code
  816. segment.  This table is merely a list of addresses to branch to, based on
  817. the value of the variable or expression being evaluated.  If the value
  818. being tested is 1, then a branch is taken to the first label in the list. 
  819. If it is 2, the code at the second label is executed, and so forth.
  820.    As many as 60 labels can be listed in an ON GOTO statement, although the
  821. number being tested can range from 0 to 255.  If the value is 0 or higher
  822. than the number of items in the list, the ON GOTO command is ignored, and
  823. execution resumes with the statement following the ON GOTO.  Negative
  824. values or values higher than 255 cause an "Illegal function call" error. 
  825. A simple example showing the basic syntax for ON GOTO is shown below.
  826.  
  827.  
  828. INPUT "Enter a value between 1 and 3: ", X
  829. ON X GOTO Label1, Label2, Label3
  830. PRINT "Illegal entry!"
  831. END
  832.  
  833. Label1:
  834.   PRINT "You pressed 1"
  835.   END
  836.  
  837. Label2:
  838.   PRINT "You pressed 2"
  839.   END
  840.  
  841. Label3:
  842.   PRINT "You pressed 3"
  843.   END
  844.  
  845.  
  846. Notice that the more labels there are, the bigger the savings in code size. 
  847. ON GOTO adds a fixed overhead of 70 bytes, 61 of which is the size of the
  848. library routine that evaluates the value and actually jumps to the code at
  849. the appropriate label.  The remaining 9 bytes are needed to load the value
  850. being tested and pass that on to the ON GOTO routine.  However, for each
  851. label in the list, only 2 bytes are required in the lookup table to hold
  852. the address.
  853.    Compare that to SELECT CASE which requires 6 bytes of set-up code (when
  854. an integer is being tested), and 13 bytes more to process each CASE.  Thus,
  855. the crossover point at which ON GOTO is more efficient is when there are
  856. 6 or more comparisons.  Notice that if ON GOTO is used in more than one
  857. place in a program, the savings are even greater because the 61-byte
  858. library routine is added only once.
  859.    Again, ON GOTO has the important restriction that all of the values must
  860. be sequential.  However, this limitation can also be turned into a feature
  861. by taking advantage of the inherent efficiency of lookup tables.
  862.    Using a lookup table is a very powerful technique, because you can
  863. determine a result using an index rather than actually calculating the
  864. answer.  A lookup table is commonly used to determine log and factorial
  865. functions, since those calculations are particularly tedious and time
  866. consuming.  With a lookup table you would calculate all of the values once
  867. ahead of time, and fill an array with the answers.  Then, to determine the
  868. factorial for, say, the number 14, you would simply read the answer from
  869. the fourteenth element in the array.
  870.    You can apply this same technique in BASIC using a combination of INSTR
  871. and ON GOTO or ON GOSUB.  Although INSTR is intended to find the position
  872. of one string within another, it is also ideal for looking up characters
  873. in a table.  Imagine you have written an input routine that must handle a
  874. number of different keys, and branch according to which one was pressed. 
  875. One way would be to use an IF/ELSEIF or SELECT CASE block, with one section
  876. devoted to each possible key.  But as you saw earlier, once there are more
  877. than 5 keys to be recognized, either of those constructs are less efficient
  878. than ON GOTO.
  879.    The approach I often use is to combine INSTR and ON GOSUB to branch
  880. according to which function key was pressed.  The beauty of this method is
  881. that a value of zero (or one that is out of range) causes control to fall
  882. through to the next statement.  Therefore any keys that are not explicitly
  883. being tested for are simply ignored.  This is shown in context below.
  884.  
  885.  
  886. DO
  887.  
  888.   DO                      'wait for a key press
  889.     K$ = INKEY$
  890.     Length% = LEN(K$)
  891.   LOOP UNTIL Length%
  892.  
  893.   IF Length% = 2 THEN     'it's an extended key
  894.     Code$ = RIGHT$(K$, 1) 'isolate the key code and branch accordingly
  895.     ON INSTR(";<=>?@ABCD", Code$) GOSUB ...
  896.   END IF
  897.  
  898. LOOP UNTIL K$ = CHR$(27)  'until they press Esc
  899.  
  900.  
  901. Here, extended keys are identified by a length of 2, and the key code is
  902. then isolated with RIGHT$.  The punctuation and letters within the quotes
  903. are characters 59 through 68, which correspond to the extended codes for
  904. F1 through F10.  (A list of all the extended key codes is in your BASIC
  905. owner's manual.)  Of course, any arbitrary list of key codes could be used. 
  906. Further, the key codes do not need to be contiguous.  For example, to
  907. branch on the Up arrow, Down arrow, Ins, Del, PgUp, and PgDn keys you would
  908. use "HPRSIQ" as the source string.  Any other mix of characters could also
  909. be used, including Alt keys.
  910.    Another interesting and clever trick that combines INSTR and ON GOTO
  911. lets you test multiple keys regardless of capitalization. The short program
  912. below accepts a character, and uses INSTR to look it up in a table of upper
  913. and lower case character pairs.
  914.  
  915.  
  916. PRINT "Yes/No/Load/Save/Retry/Quit? "; 
  917.  
  918. DO 
  919.   K$ = INKEY$ 
  920. LOOP UNTIL LEN(K$) = 1 
  921.  
  922. ON (INSTR("YyNnLlSsRrQq", K$) + 1) \ 2 GOTO ... 
  923.  
  924.  
  925. After adding 1 and dividing that by 2, the result will indicate in which
  926. character pair the choice was found.  This technique could also be extended
  927. to include 3- or 4-character groups, or any other combination of
  928. characters.  Since any value between 0 and 255 is legal for an ASCII
  929. character, INSTR can be used in other, more general lookup situations as
  930. well.
  931.  
  932.  
  933. A COMPARISON OF SUBROUTINE METHODS
  934. ==================================
  935.  
  936. There are four primary subroutine types that BASIC supports: GOSUB
  937. subroutines, DEF FN functions, called subprograms, and what I refer to as
  938. "formal functions".  Each has its own advantages and disadvantages, which
  939. I will describe momentarily.  But I would first like to introduce several
  940. terms that will be used throughout the discussion that follows.
  941.    The first is *module*, which is a series of BASIC program statements
  942. kept in their own separate source file.  All modules have a main portion,
  943. and some also have procedures within a SUB or FUNCTION block.  The main
  944. portion of a program is that which receives control when the program is
  945. first run.  When a program is comprised of multiple modules, each
  946. additional module has a main portion, although code within that portion is
  947. rarely executed.  In fact, there are only two ways to access code in the
  948. main portion of an ancillary module:  One is to create a line label and use
  949. that as the target for ON ERROR or another "ON" event.  The other is to
  950. define a DEF FN function and invoke the function.
  951.    The second term is *variable scope*, which indicates where in a program
  952. a variable may be accessed.  Variables that are used in the main portion
  953. of a program are accessible anywhere else in the main, but not within a SUB
  954. or FUNCTION block.  Likewise, a variable that is defined within a SUB or
  955. FUNCTION is by default private to that procedure.  The overwhelming
  956. advantage of private variables is that you do not have to worry about
  957. errors caused by inadvertently using the same variable name twice.
  958.    The third term is *SHARED*, and it overrides the default private scope
  959. of a variable used in a procedure.  SHARED may be used in either of two
  960. ways.  If it is specified with a DIM statement in the main body of a
  961. program--that is, DIM SHARED Variable--the variable is established as being
  962. shared throughout the entire source file.  Even though DIM is usually
  963. associated with arrays, it can be used this way to extend a variable's
  964. scope.
  965.    SHARED may also be used within a subroutine to share one or more
  966. variables with the main portion.  Notice that the statement SHARED Variable
  967. inside a procedure defines the variable as being shared with the main
  968. portion of the program only.  SHARED used within a procedure does not share
  969. the named variable with any other procedures.  The only exception is when
  970. other procedures also use SHARED with the same variable name.  In that case
  971. they are shared between procedures, as well as with the main program.
  972.        ╔═════════════════════════════╗
  973.        ║   DEFINT A-Z                ║
  974.        ║   DIM SHARED Var1           ║
  975.        ║                             ║
  976.     ┌──╫──>Var1 = 100                ║
  977.  ┌──│──╫──>Var2 = 200                ║
  978.  │  │  ║   CALL Sub1(Var2)           ║
  979.  │  │  ║   CALL Sub2(Var2)           ║
  980.  │  │  ║   END                       ║
  981.  │  │  ║                             ║
  982.  │  │  ║   SUB Sub1 (Param) STATIC   ║
  983.  │  ├──╫────>Var1 = Param            ║
  984.  │  │  ║     Var2 = Var1             ║
  985.  │  │  ║   END SUB                   ║
  986.  │  │  ║                             ║
  987.  │  │  ║   SUB Sub2 (Param) STATIC   ║
  988.  │  │  ║     SHARED Var2             ║
  989.  │  └──╫────>Var1 = Param            ║
  990.  └─────╫────>Var2 = Var1             ║
  991.        ║   END SUB                   ║
  992.        ╚═════════════════════════════╝
  993.   
  994. Figure 3-1: How SHARED and DIM SHARED affect variable scope.  Variables
  995. that share the same identity are shown connected.
  996.  
  997. The fourth term is *COMMON*, which is related to SHARED in that it also
  998. lets you share variables among procedures.  However, COMMON has the
  999. additional property of allowing variables to be shared by procedures that
  1000. are not in the same physical source file.  When BC compiles your program,
  1001. it translates your variable names to memory addresses.  Thus, those names
  1002. are not available when the program is linked to other object files. 
  1003. Variables that are listed in a COMMON statement are placed in a separate
  1004. portion of the data segment which is reserved just for that purpose. 
  1005. Therefore, other program modules using COMMON can also access those
  1006. variables in that portion of DGROUP.
  1007.                  MODULE1.BAS
  1008.        ╔═════════════════════════════╗
  1009.        ║   DEFINT A-Z                ║
  1010.        ║   COMMON SHARED Var1        ║
  1011.        ║                             ║
  1012.  ┌─────╫──>Var1 = 100                ║
  1013.  │  ┌──╫──>Var2 = 200                ║
  1014.  │  │  ║   CALL Sub1(Var2)           ║
  1015.  │  │  ║   CALL Sub2(Var2)           ║
  1016.  │  │  ║   END                       ║
  1017.  │  │  ║                             ║
  1018.  │  │  ║   SUB Sub1 (Param) STATIC   ║
  1019.  ├──│──╫────>Var1 = Param            ║
  1020.  │  │  ║     Var2 = Var1             ║
  1021.  │  │  ║   END SUB                   ║
  1022.  │  │  ║                             ║
  1023.  │  │  ║   SUB Sub2 (Param) STATIC   ║
  1024.  │  │  ║     SHARED Var2             ║
  1025.  ├──│──╫────>Var1 = Param            ║
  1026.  │  └──╫────>Var2 = Var1             ║
  1027.  │     ║   END SUB                   ║
  1028.  │     ╚═════════════════════════════╝
  1029.  │   
  1030.  │               MODULE2.BAS
  1031.  │     ╔═════════════════════════════╗
  1032.  │     ║   DEFINT A-Z                ║
  1033.  │     ║   COMMON Var1               ║
  1034.  │     ║                             ║
  1035.  └─────╫──>Var1 = 100                ║
  1036.     ┌──╫──>Var2 = 200                ║
  1037.     │  ║   CALL Sub1(Var2)           ║
  1038.     │  ║   CALL Sub2(Var2)           ║
  1039.     │  ║   END                       ║
  1040.     │  ║                             ║
  1041.     │  ║   SUB Sub1 (Param) STATIC   ║
  1042.     │  ║     Var1 = Param            ║
  1043.     │  ║     Var2 = Var1             ║
  1044.     │  ║   END SUB                   ║
  1045.     │  ║                             ║
  1046.     │  ║   SUB Sub2 (Param) STATIC   ║
  1047.     │  ║     SHARED Var2             ║
  1048.     │  ║     Var1 = Param            ║
  1049.     └──╫────>Var2 = Var1             ║
  1050.        ║   END SUB                   ║
  1051.        ╚═════════════════════════════╝
  1052.  
  1053. Figure 3-2: How COMMON and COMMON SHARED affect variable scope.  Variables
  1054. that share the same identity are shown connected.
  1055.  
  1056. COMMON can also be combined with SHARED, to specify that one or more
  1057. variables be shared throughout the main program as well as with other
  1058. modules.  That is, the statement COMMON SHARED Variable tells BASIC that
  1059. Variable is to be both DIM SHARED and COMMON.  To establish a TYPE variable
  1060. as COMMON, you must state the type name as well: COMMON TypeVar AS MyType. 
  1061. In all cases, COMMON statements must precede the executable statements in
  1062. a program.  The only statements that may appear before COMMON are other
  1063. non-executable statements such as DECLARE, CONST, and '$STATIC.
  1064.    Because the variable names listed in a COMMON statement are not stored
  1065. in the final program, the names used in one module do not need to be the
  1066. same as the corresponding names in another module.  You could, for example,
  1067. have COMMON X%, Y$, Z# in one file, and COMMON A%, B$, C# in another. 
  1068. Here, X% refers to the same memory location as A%; Y$ is the same variable
  1069. as B$, and so forth.  It is imperative, however, that the order and type
  1070. of variables match.  If one file has an integer followed by a string
  1071. followed by a double precision variable, then all other files containing
  1072. a COMMON statement must have their COMMON variables in that same order.  
  1073. This is one good reason for storing all COMMON statements in a single
  1074. include file, which is included by each module that needs access to the
  1075. COMMON variables.
  1076.    One or more arrays may also be listed as COMMON; however, the rules are
  1077. different for static and dynamic arrays.  When a dynamic array is to made
  1078. COMMON, it should be dimensioned in the main program only, following the
  1079. COMMON statement.  (But you may use REDIM in another module if necessary,
  1080. to change the array's size.)  Static arrays must be dimensioned in each
  1081. module, before the associated COMMON declaration.  Of course, all array
  1082. types must match across modules--you may not list a static array as the
  1083. first COMMON item in one file, and then list a dynamic array in that same
  1084. position in another file.
  1085.    There are actually two forms of COMMON statement: the blank COMMON and
  1086. the named COMMON.  The examples shown thus far are blank COMMON statements. 
  1087. A named COMMON block lets you specify selected variable groups as COMMON,
  1088. to avoid having to list many variables when all of them are not needed in
  1089. a given module.  A COMMON block is named by preceding the variable list
  1090. with a name surrounded by slash characters.  For instance, this line:
  1091.  
  1092.    COMMON /IntVars/ X%, Y%, Z%
  1093.  
  1094. establishes a named COMMON black called IntVars.  By creating several such
  1095. named blocks you may share only those that are actually needed in a given
  1096. module.
  1097.    In this case, the block name is stored in the object file, and LINK
  1098. ensures that the COMMON variables in each module share the same addresses. 
  1099. One important limitation of a named COMMON block is that it cannot be used
  1100. to pass information between programs that use CHAIN.
  1101.    The fifth term is *STATIC*, which I described in a slightly different
  1102. context in the section about data in Chapter 2.  When you add the STATIC
  1103. option to a SUB or FUNCTION definition, BASIC treats the variables within
  1104. that procedure very differently than when STATIC is omitted.  With STATIC,
  1105. memory in DGROUP is allocated by the compiler for each variable, and that
  1106. memory is permanently reserved for use by those variables.
  1107.    When STATIC is not specified, the variables in the routine are by
  1108. default placed onto the system stack.  This means that sufficient stack
  1109. memory must be available, although that memory can then be used again later
  1110. for variables in other procedures.  An important side effect of using the
  1111. stack for variable storage is that the memory is cleared each time the
  1112. subprogram or function is entered.  Therefore, all numeric variables are
  1113. initialized to zero, and strings are initialized to null.  Any arrays
  1114. within a non-static procedure are by default dynamic, which means they are
  1115. created upon entry to the routine and erased when the routine exits.
  1116.    STATIC also has an additional meaning in subprograms and functions; it
  1117. can establish variables as being private to a procedure.  If a variable has
  1118. been declared as shared throughout a module by using DIM SHARED in the main
  1119. portion of the program, using the statement STATIC Variable inside the
  1120. subroutine will override that property.  Thus, Variable will be local to
  1121. the procedure, and will not conflict with a global shared variable of the
  1122. same name.  STATIC within a subprogram or function also lets you use the
  1123. same name for a variable that was already given to a named constant.
  1124.    Many programmers find the use of the term STATIC for two very different
  1125. purposes confusing, and rightly so.  It would have made more sense to use
  1126. a different keyword, perhaps LOCAL, to limit a variable's scope.  And to
  1127. further confuse the issue, the '$STATIC metacommand is used to establish
  1128. the memory storage method for arrays.  None the less, STATIC always
  1129. indicates that memory for a variable is permanently allocated, and it may
  1130. also specify that a variable is private to a procedure.
  1131.    The final term I want to introduce now is *recursion*.  The classic
  1132. definition of a recursive procedure is that it may call itself.  While this
  1133. is certainly true, that doesn't really explain what recursion is all about,
  1134. or how it could be useful.  I will cover recursion in depth momentarily,
  1135. but for now suffice it to say that recursion is often helpful when
  1136. manipulating tree-structured information.
  1137.    For example, a program that lists all of the files on a hard disk would
  1138. most likely be based on a recursive subroutine.  Such a program would first
  1139. change to the root directory, and then call the routine to read and display
  1140. all of the file names it finds there.  Then for each directory under the
  1141. current one, the routine would change to that directory and call itself
  1142. again to read and display the files in that directory.  And if more
  1143. directories were found at the next level down, the routine would call
  1144. itself yet again to process all of those files too.  This continues until
  1145. all of the files in all directories on the hard disk have been processed.
  1146.    Another application for recursion is a subroutine that sorts an array
  1147. on more than one key.  For example, consider a TYPE array in which each
  1148. element has components for a first name, a last name, and address fields. 
  1149. You might want to be able to sort that array first by last name, then by
  1150. first name, and then by zip code.  That is, all of the Smiths would be
  1151. grouped together, and within that group Adam would be listed before John. 
  1152. All of the John Smiths would in turn be sorted in zip code order.
  1153.    By employing recursion, the routine would first sort the entire array
  1154. based on the last name only.  Next, it would identify each range of
  1155. elements that contain identical last names.  The routine would then call
  1156. itself to sort that subgroup, and call itself again to sort the subgroup
  1157. within that group based on zip code.
  1158.  
  1159.  
  1160. SUBROUTINES VERSUS FUNCTIONS
  1161.  
  1162. There is a fundamental difference between subroutines and functions.  A
  1163. subroutine is accessed with either a CALL or GOSUB statement, and a
  1164. function is invoked by referencing its name.  In general, a subroutine is
  1165. used to perform an action such as opening a group of files, or perhaps
  1166. updating a screen-full of information.  A function, on the other hand,
  1167. returns a value such as the result of a calculation.  A string function
  1168. also returns information, although in this case that information is a
  1169. string.
  1170.    Notice that the type of information returned by a function is
  1171. independent of the type of parameters, if any, that are passed to it.  For
  1172. example, BASIC's native STR$ function accepts a numeric argument but
  1173. returns a string.  Likewise, a numeric function such as INSTR accepts two
  1174. strings and returns a single integer.  This is also true for functions that
  1175. you design using either DEF FN or FUNCTION.
  1176.    Although a function is primarily used for calculations and a subroutine
  1177. for performing one or more actions, there is no hard and fast distinction
  1178. between the two.  You could easily design a subroutine that multiplies
  1179. three numbers and returns the answer in one of the parameters.  Similarly,
  1180. a function could be written to clear the screen and then open a file. 
  1181. Which you use and when will depend on your own programming style.  However,
  1182. there are definite advantages to using functions where appropriate.
  1183.    One immediately obvious benefit of a function is that a value can be
  1184. returned without requiring an additional passed parameter.  Each variable
  1185. that is passed as a parameter requires 4 bytes of code for setup, plus an
  1186. additional 5 bytes within the subroutine each time it is accessed.
  1187.    Another important advantage of using a function is BASIC's automatic
  1188. type conversion.  If you assign a single precision variable from the result
  1189. of an integer function, BASIC will convert the data from one format to the
  1190. other transparently.  In fact, a simple assignment from a variable of one
  1191. type to that of another type is also handled for you by the compiler.  But
  1192. if a routine is written to pass the value back as a parameter, then you
  1193. must use whatever type of data the subprogram expects.
  1194.    Although most high-level languages require the programmer to match
  1195. explicitly the types of data being assigned, Microsoft BASIC has done this
  1196. automatically since its inception.  When you write Var1! = Var2%, BASIC
  1197. treats that as Var1! = CSNG(Var2%).  Object oriented programming languages
  1198. use the term *polymorphism* to describe such automatic type conversion.
  1199.  
  1200.  
  1201. GOSUB ROUTINES
  1202.  
  1203. The primary advantage a GOSUB routine holds over all of the other
  1204. subroutine types is that it can be accessed very quickly.  Translated to
  1205. assembly language a GOSUB statement is but three bytes in length, and its
  1206. speed is surpassed only by a GOTO.  When the only thing that matters is how
  1207. fast a subroutine can be called, GOSUB has the clear advantage.  However,
  1208. there are many limitations inherent in a GOSUB.
  1209.    The most important restriction is that arguments cannot be passed using
  1210. GOSUB.  Therefore, any variables must be assigned before invoking the
  1211. routine, and possibly reassigned when it returns.  For example, if a
  1212. subroutine requires two parameters--perhaps a row and column at which to
  1213. print a message--those variables must be assigned before the GOSUB can be
  1214. used.  And if a value is being returned, your program must know the name
  1215. of the variable that was assigned within the GOSUB routine.
  1216.    Another important limitation is that the target line label must be in
  1217. the same block of code as the GOSUB.  Although a GOSUB is legal within a
  1218. SUB or FUNCTION, both the GOSUB and the routine it calls must be located
  1219. in the same procedure.  Likewise, a GOSUB in the main body of a program
  1220. cannot access a subroutine inside a procedure, or vice versa.  [And of
  1221. course you cannot invoke a GOSUB routine that is located in a different
  1222. source module.]
  1223.    Both of these problems restrict your ability to reuse a subroutine in
  1224. more than one program.  One of the goals of modern structured programming
  1225. is the ability to design a routine for one application, and also use it
  1226. again later in other programs.  The only way to do that using GOSUB
  1227. routines is to establish a variable naming convention, and always use
  1228. variables and line labels with those unique names.
  1229.  
  1230.  
  1231. SUBPROGRAMS
  1232.  
  1233. Subprograms were introduced with QuickBASIC version 2.0, and they improve
  1234. greatly on GOSUB routines in many respects.  The most important advantages
  1235. of a subprogram are that it accepts passed parameters, and that variables
  1236. used within the subprogram are local by default.  Besides the obvious
  1237. benefit of not having to worry about variable naming conflicts, these
  1238. properties allow you to create your own toolbox of useful subroutines, and
  1239. use them repeatedly in different programming projects.  I will discuss this
  1240. use of subprograms in detail later in this chapter.
  1241.    A subprogram is accessed using the CALL statement, and any number of
  1242. arguments may optionally be passed to the routine.  A subprogram is defined
  1243. with a statement of the form SUB SubName (Param1, Param2, ...) STATIC.  The
  1244. parameters and surrounding parentheses are optional, as is the STATIC
  1245. directive.  Of course, the number of arguments passed to a subprogram must
  1246. match the number of parameters it expects.
  1247.    As you can see, subprograms have many advantages over GOSUB routines. 
  1248. However, they are not a magical panacea for every programming problem. 
  1249. Each subprogram includes a fixed amount of overhead just to enter and exit
  1250. it.  Because of the complexities of accessing incoming parameters, a *stack
  1251. frame* must be created by the compiler upon entry.  A stack frame is simply
  1252. a fancy name for an area of memory that holds the addresses of the incoming
  1253. parameter.  However, this requirement adds a fair amount of code to each
  1254. subprogram.
  1255.    Eight bytes of code are needed to set up and call the internal BASIC
  1256. routine that creates the stack frame, and the routine itself comprises
  1257. another 35 bytes.  Eight more bytes are needed to call the routine that
  1258. exits a subprogram, and that routine adds contains 26 bytes.  Finally, all
  1259. but the last subprogram in a source file needs a 3-byte jump to skip over
  1260. the other subprograms that follow.  Therefore, a total of 80 bytes are
  1261. added to any program that uses a subprogram rather than a GOSUB routine. 
  1262. It is important to point out, however, that the 61 bytes used by the
  1263. library routines to enter and exit a subprogram are added to the final .EXE
  1264. file only once.
  1265.    It is also worth mentioning that BASIC PDS provides the /Ot switch,
  1266. which eliminates the usual overhead incurred from calling the routines
  1267. needed to enter and exit a subprogram.  Although using /Ot avoids the code
  1268. that is otherwise added, there is one important restriction:  You may not
  1269. use a GOSUB within the subprogram.  When a program performs a GOSUB, the
  1270. address to return to is placed onto the stack, for retrieval later when the
  1271. subroutine returns.  Likewise, when a subprogram is called, both a segment
  1272. and address to return to are put on the stack.
  1273.    If a GOSUB were used inside the subprogram and an EXIT SUB was then
  1274. encountered within the GOSUBed subroutine, the return addresses on the
  1275. stack would be out of order.  Thus, the subprogram would return to the
  1276. wrong place, with undoubtedly disastrous consequences.  To avoid this,
  1277. BASIC by default saves the address to return to when the subprogram is
  1278. first entered, and uses that when it is exited.  Therefore, when the
  1279. compiler sees that a GOSUB is being used, it does not use the abbreviated
  1280. method even if /Ot has been specified.
  1281.    Although using /Ot makes a subprogram (and function) much faster by
  1282. eliminating the overhead to call the entry and exit routines, there is no
  1283. actual savings in code size.  A series of assembler NOP (No Operation)
  1284. instructions are placed where the entry and exit code would have been. 
  1285. However, those empty instructions are never executed.  We can only hope
  1286. that in future releases of BASIC PDS Microsoft will improve BC's code
  1287. generation to eliminate these unnecessary instructions.  [Yeah, right.]
  1288.    Another problem with subprograms is that programmers tend to use them
  1289. to excess.  For example, I have seen people create subprograms to increment
  1290. and decrement integer variables even though it is far more efficient to do
  1291. that with in-line code.  The statement X% = X% + 1 creates only 4 bytes of
  1292. code, compared to 9 for a single call to a subprogram to do the same thing! 
  1293. However, incrementing long integer or floating point variables does take
  1294. more code than invoking a subprogram with a single parameter, so a
  1295. subprogram could be useful in that case.  Only by counting the number of
  1296. times a subprogram will be used and comparing that to the overhead incurred
  1297. can you determine whether there will be any savings.
  1298.  
  1299.  
  1300. DEF FN FUNCTIONS
  1301.  
  1302. Although a DEF FN function is designed to return a result, it is more
  1303. closely related to a GOSUB subroutine in actual operation.  Like a GOSUB
  1304. routine it is invoked with a 3-byte assembly language "near" call, as
  1305. opposed to the 5-byte "far" call that subprograms and formal functions
  1306. require.  And while a DEF FN function can accept incoming parameters,
  1307. variables within the function definition are by default shared with the
  1308. main portion of the program.
  1309.    As I already explained, variables used in a DEF FN function can be made
  1310. private to the function only by explicitly declaring them as STATIC. 
  1311. However, at least it is possible to employ local variables.  Further, a DEF
  1312. FN function can return a result, which makes it an ideal replacement for
  1313. GOSUB when speed is paramount.
  1314.    Internally, parameters are passed to a DEF FN function very differently
  1315. than to a called subprogram or formal function.  Arguments are passed to
  1316. a subprogram by placing their addresses on the stack.  With a DEF FN
  1317. function, however, a copy of each parameter is created, and the function
  1318. directly manipulates those copies.  Therefore, it is impossible for a DEF
  1319. FN function to modify an incoming parameter directly.  This behavior is
  1320. neither good nor bad.  Rather, it is simply different and thus important
  1321. to understand.  It is also important to understand that a DEF FN function
  1322. can be used only in the module in which it is defined.  If the same
  1323. function is needed in different modules, the same code must be duplicated
  1324. again and again.
  1325.    In the manuals that come with QuickBASIC and BASIC PDS, Microsoft
  1326. advises against using DEF FN functions, in favor of the newer, more
  1327. powerful formal functions.  Because of this favoritism, Microsoft will
  1328. probably never correct one disturbing anomaly that is present in all DEF
  1329. FN functions.  When a string is passed as an argument to a DEF FN function,
  1330. a copy is made for the function to manipulate.  Unfortunately, the copy is
  1331. never deleted!  Therefore, if you pass, say, a 10,000 byte string to a DEF
  1332. FN function, that amount of memory is permanently taken until the function
  1333. is invoked again later.  The short listing below proves this behavior.
  1334.  
  1335.  
  1336. DEF FnWaste (A$)
  1337.   FnWaste = ASC(A$)
  1338. END DEF
  1339.  
  1340. Big$ = SPACE$(10000)
  1341. PRINT FRE(Big$)
  1342. X = FnWaste(Big$)
  1343. PRINT FRE(Big$)
  1344.  
  1345.  
  1346. Notice that running this program in the QuickBASIC editing environment will
  1347. not give the expected (memory-wasting) result.  However, in a separately
  1348. compiled program the 10000 byte loss will be evident.
  1349.    As with subprograms, there is a fixed amount of overhead required to
  1350. enter and exit a DEF FN function.  For each function that has been defined,
  1351. 5 bytes are needed to call the Enter and Exit routines.  Further, these
  1352. routines are 14 and 24 bytes in length respectively.  But again, the
  1353. routines themselves are added to a program only once when it is linked.
  1354.    There are two final limitations of DEF FN functions worth mentioning
  1355. here.  The first is that arrays and TYPE variables may not be passed as
  1356. parameters to them.  Since by design a copy is made of every incoming
  1357. parameter, there is no reasonable way to do that with an entire array.  The
  1358. second limitation is that the function definition must be physically
  1359. positioned in the source file before any references are made to it.
  1360.  
  1361.  
  1362. FORMAL FUNCTIONS
  1363.  
  1364. A formal function is nearly identical to a called subprogram, and it
  1365. requires the exact same amount of overhead to enter and exit.  Also like
  1366. subprograms, nearly any type of data may be passed to a function, including
  1367. TYPE variables and arrays.  The only limitation is that a fixed-length
  1368. string may not be used directly as a parameter.  If a fixed-length string
  1369. is passed to a subprogram or function that expects a string, a copy is made
  1370. and assigned to a conventional string.  This copying was described in
  1371. detail in Chapter 2.
  1372.    Because a formal function is invoked by referencing its name in an
  1373. assignment or PRINT statement, it is essential that it be declared.  After
  1374. all, how else could BASIC know that the statement PRINT MyFunc means to
  1375. call a function and display the result, as opposed to printing the variable
  1376. named MyFunc?  When a BASIC function is created in the BASIC editing
  1377. environment, a corresponding DECLARE statement is generated automatically. 
  1378. But when a function is written in another language or kept in a Quick
  1379. Library, an explicit declaration is mandatory.
  1380.    Like subprograms, formal functions are ideally suited to modular,
  1381. reusable programming methods.  Furthermore, a function may be accessed from
  1382. any module in an entire application, even those in other source files. 
  1383. Indeed, the only difference between a subprogram and a function is that a
  1384. function returns a result.  The assembly language code that BASIC generates
  1385. is in all other respects identical.
  1386.  
  1387.  
  1388. STATIC VERSUS NON-STATIC PROCEDURES
  1389.  
  1390. As I stated earlier, when the STATIC keyword is appended to a SUB or
  1391. FUNCTION declaration, all of the variables within the routine are assigned
  1392. a permanent address in DGROUP.  And when STATIC is omitted, the variables
  1393. are instead stored on the stack and cleared to zeros or null strings each
  1394. time the routine is entered.  There are several important ramifications of
  1395. this behavior.  Non-static procedures allocate new stack memory each time
  1396. they are invoked, and then release that memory when they exit.  It is
  1397. therefore possible to exhaust the available stack space when the subroutine
  1398. calls are deeply nested.
  1399.    For example, if you call one subprogram that then calls another which
  1400. in turns calls yet another, sufficient stack memory must be available for
  1401. all of the variables in all of the subprograms.  Besides the memory needed
  1402. for each variable in a subprogram or function, other data is also placed
  1403. onto the stack as part of the call.  For each parameter that is passed, 2
  1404. bytes are taken to hold its address.  Add to that 4 bytes to store the
  1405. segment and address to return to in the calling program.  Finally,
  1406. temporary variables that BASIC creates for its own purposes are also stored
  1407. on the stack in a non-static subprogram or function.
  1408.    Another important consideration when STATIC is omitted is that every
  1409. string variable must be deleted before the subprogram exits.  Because of
  1410. the way BASIC's string management routines operate, memory that holds
  1411. string descriptors and string data cannot simply be abandoned.  Every
  1412. string must be released explicitly by a called routine, at a cost of 9
  1413. bytes per string.  Please understand that you do not have to delete these
  1414. strings.  Rather, this is another case where BASIC creates additional code
  1415. without telling you.
  1416.    Again, I would love to be able to tell you that using STATIC is always
  1417. desirable, or that never using it always makes sense.  But unfortunately,
  1418. it just isn't that simple.  When a program becomes very large and complex,
  1419. only by counting variables can you be absolutely certain how much stack
  1420. space is really needed.  Although the FRE(-2) function may be used to
  1421. determine how much stack memory is currently available, it does not tell
  1422. how much memory is actually needed by each routine.
  1423.    To summarize the trade-offs between static and non-static variables:
  1424. Static variables are allocated permanently by the compiler, and the memory
  1425. they occupy can never be used for any other purpose.  Non-static variables
  1426. are placed onto the stack, and exist only while the subprogram or function
  1427. is in use.  Remember that you can also have a mix of static and non-static
  1428. variables in the same procedure.  By omitting STATIC after the subroutine
  1429. name, all variables will by default be non-static.  You can then override
  1430. that property for selected variables by using the STATIC keyword.  In the
  1431. section on debugging in Chapter 4, you will learn how to use CodeView to
  1432. determine the stack requirements for a procedure's variables.
  1433.  
  1434.  
  1435. Controlling the Stack Size
  1436.  
  1437. There are several ways to control the amount of memory that is dedicated
  1438. for use by the stack.  All versions of BASIC support the CLEAR command,
  1439. which takes an optional argument that sets the stack size.  The statement
  1440. CLEAR , , StackSize sets aside StackSize bytes for the stack. 
  1441. Unfortunately, CLEAR also clears all of the data in a program, closes any
  1442. open files, and erases all arrays.  If you know ahead of time how much
  1443. stack memory will be needed, then using CLEAR as the first statement in a
  1444. program will not cause a problem.
  1445.    Even when CLEAR is used as the first statement in a program, there is
  1446. still one situation where that will not be acceptable.  When you use CHAIN
  1447. to execute a subsequent program, a CLEAR statement in that program will
  1448. clear all of the variables that have been declared COMMON.  Fortunately,
  1449. there are two solutions to this problem: BASIC PDS offers the STACK
  1450. statement, which lets you establish the size of the stack but without the
  1451. side effects of CLEAR.  For example, the statement STACK 5000 sets aside
  1452. 5000 bytes for the stack.  The other solution is to use the /STACK: link
  1453. switch, which reserves a specified number of bytes.  All of the options
  1454. that LINK supports are described in Chapter 5.
  1455.  
  1456.  
  1457. RECURSION
  1458.  
  1459. I have already illustrated some of the situations in which a recursive
  1460. subprogram or function could be useful.  Now lets look at some actual
  1461. programming examples.  The Evaluate function in the listing below uses
  1462. recursion to reinvoke itself for each new level of parentheses it
  1463. encounters.
  1464.  
  1465.  
  1466. DECLARE FUNCTION Evaluate# (Formula$)
  1467.  
  1468. INPUT "Enter an expression: ", Expr$
  1469. PRINT "That evaluates to"; Evaluate#(Expr$)
  1470.  
  1471. FUNCTION Evaluate# (Formula$)
  1472.  
  1473.   'Search for an operator using INSTR as a table lookup.  If found,
  1474.   'remember which one and its position in the string.
  1475.   FOR Position% = 1 TO LEN(Formula$)
  1476.     Operation% = INSTR("+-*/", MID$(Formula$, Position%, 1))
  1477.     IF Operation% THEN EXIT FOR
  1478.   NEXT
  1479.  
  1480.   'Get the value of the left part, and a tentative value for the
  1481.   'right part.
  1482.   LeftVal# = VAL(Formula$)
  1483.   RightVal# = VAL(MID$(Formula$, Position% + 1))
  1484.  
  1485.   'See if there's another level to evaluate.
  1486.   Paren% = INSTR(Position%, Formula$, "(")
  1487.  
  1488.   'There is, call ourselves for a new RightVal#.
  1489.   IF Paren% THEN RightVal# = Evaluate#(MID$(Formula$, Paren% + 1))
  1490.  
  1491.   'No more to evaluate, do the appropriate operation and exit.
  1492.   SELECT CASE Operation%
  1493.     CASE 1                      'addition
  1494.       Evaluate# = LeftVal# + RightVal#
  1495.     CASE 2                      'subtraction
  1496.       Evaluate# = LeftVal# - RightVal#
  1497.     CASE 3                      'multiplication
  1498.       Evaluate# = LeftVal# * RightVal#
  1499.     CASE 4                      'division
  1500.       Evaluate# = LeftVal# / RightVal#
  1501.    END SELECT
  1502.  
  1503. END FUNCTION
  1504.  
  1505.  
  1506. When you run this program, enter an expression like 15 * (12 + (100 / 8)). 
  1507. To keep the code to a minimum, Evaluate accepts only simple, two-number
  1508. expressions.  That is, it will not work with more than one math operator
  1509. within each pair of parentheses as in 10 * (3 + 4 + 5).  However, the
  1510. parentheses may be nested to nearly any level.
  1511.    This function begins by examining each character in the incoming formula
  1512. string for a math operator.  If it finds one the operator number (1 through
  1513. 4) is remembered, as well as its position in the formula string.  Next, VAL
  1514. is used to obtain the value of the digits to the left of the operator, as
  1515. well as the digits to the right.  Notice that it was not necessary to use
  1516. LEFT$ to isolate the left-most portion of the string, because VAL stops
  1517. examining the string when it encounters any non-digit character such as the
  1518. "+" or "(".
  1519.    Once these values have been saved, the next test determines if any more
  1520. parentheses follow in the formula.  If so, Evaluate calls itself, passing
  1521. only those characters that are beyond the next parenthesis.  Thus, the same
  1522. routine evaluates each new level, returning to the level above only after
  1523. all levels have been examined.  I encourage you to run this program in the
  1524. QuickBASIC editing environment, and step through each statement one by one
  1525. with the F8 Trace command.  In particular, use the Watch Variable feature
  1526. to view the value of Position% and LeftVal# as the function recurses into
  1527. subsequent invocations.
  1528.    It is important to understand the need for stack variables in this
  1529. program, and why STATIC must not be used in the function definition.  When
  1530. Evaluate walks through the incoming string and determines which math
  1531. operator is specified, that operator must be remembered throughout the
  1532. course of the function.  If a static variable were used for Operation%,
  1533. then its previous value would be destroyed when Evaluate calls itself. 
  1534. Likewise, LeftVal# cannot be overwritten either, or it would not hold the
  1535. correct value when Evaluate returns to itself from the level below. 
  1536. Therefore, as you step through this program you will observe that each new
  1537. invocation of Evaluate creates a new set of variables.
  1538.    As you can see, stack variables are necessary for the proper functioning
  1539. of a subprogram or function that calls itself.  They are also necessary
  1540. when one procedure calls another procedure which in turn calls the first
  1541. one again.  The key point is that each time a non-static routine is
  1542. invoked, new and unique variables must be created.  Otherwise, the variable
  1543. contents from a previous level above will be overwritten.
  1544.    Although recursion is a powerful and necessary technique, it should be
  1545. used only when necessary.  There is a substantial amount of overhead needed
  1546. to allocate stack memory and clear it to zeros, so invoking a non-static
  1547. routine is relatively slow.  And as I described earlier, every non-static
  1548. string variable must be deleted when the routine exits, at a cost of 9
  1549. bytes apiece.
  1550.    Some programmers use recursion even when there are other, more efficient
  1551. ways to solve a problem.  For example, the QuickBASIC manual shows a
  1552. recursive function that calculates a factorial.  (A factorial is derived
  1553. by multiplying a number by all of the whole numbers less than itself.  That
  1554. is, the factorial of 4 equals 4 * 3 * 2 * 1.)   However, a factorial can
  1555. be calculated faster and with less code using a simple FOR/NEXT loop as
  1556. shown below.  This version of Factorial is 20 percent faster than the
  1557. example given in the QuickBASIC manual.
  1558.  
  1559.  
  1560. FUNCTION Factorial#(Number%) STATIC
  1561.   Seed# = 1
  1562.   FOR X% = 1 TO Number%
  1563.     Seed# = Seed# * X%
  1564.   NEXT
  1565.   Factorial# = Seed#
  1566. END FUNCTION
  1567.  
  1568.  
  1569. PASSING PARAMETERS TO PROCEDURES
  1570.  
  1571. As you have already learned, BASIC normally passes data to a subprogram or
  1572. function by placing its address on the stack.  And when an entire array is
  1573. specified, the address of the array descriptor is sent instead.  But there
  1574. are some cases where BASIC imposes restrictions on how variables and arrays
  1575. may be passed to a procedure.  Let's look now at some of the ways to get
  1576. around those restrictions.
  1577.    When using versions of BASIC earlier than PDS 7.1, it is not legal to
  1578. pass an array of fixed-length strings.  In fact, it is also impossible to
  1579. pass a single fixed-length string directly.  As you saw in Chapter 2, BASIC
  1580. copies every fixed-length string argument to a regular string, which adds
  1581. a lot of code and also wastes string memory.
  1582.    The simplest solution for fixed-length strings is to define an
  1583. equivalent TYPE that is comprised of a single string component.  Since a
  1584. TYPE variable or array can legally be passed, this is the easiest and most
  1585. direct approach, as shown here.
  1586.  
  1587.  
  1588. TYPE FLen
  1589.   S AS STRING * 100
  1590. END TYPE
  1591. DIM MyString AS Flen
  1592. CALL Subprogram(MyString)
  1593.  
  1594. SUB Subprogram(FLString AS FLen)
  1595.   ...
  1596.   ...
  1597. END SUB
  1598.  
  1599.  
  1600. If the subprogram being called is in a separate module, then the TYPE
  1601. definition must also be present in that file.  However, the DIM statement
  1602. is needed only in the program that passes the string.  This also works with
  1603. fixed-length string arrays, except that the DIM would have to be changed
  1604. to DIM MyArray(1 TO NumElements) AS FLen, and the subprogram's definition
  1605. would be changed to SUB Subprogram(FLString() AS FLen).
  1606.    BASIC PDS 7.1 supports passing a fixed-length string array directly, so
  1607. this work-around is not needed with that version.  Curiously, a single
  1608. fixed-length string may not be passed as a parameter in BASIC 7.1.  Since
  1609. a fixed-length string is closely related to a TYPE variable, this
  1610. limitation seems arbitrary at best.
  1611.    BASIC 7.1 also supports the use of BYVAL when passing numeric arguments
  1612. to procedures.  This is a particularly powerful feature, because it can
  1613. greatly reduce the amount of code needed to access those values within the
  1614. routine.  It also eliminates the need to make copies when a constant is
  1615. passed as an argument.  To take advantage of this feature, you simply
  1616. specify BYVAL in both the calling and receiving argument list, as shown
  1617. below.
  1618.  
  1619.  
  1620. DECLARE SUB Subroutine(BYVAL Arg1%, BYVAL Arg2%)
  1621. CALL Subroutine(Var1%, Var2%)
  1622.  
  1623. SUB Subroutine(BYVAL X%, BYVAL Y%)
  1624.   ...
  1625.   ...
  1626. END SUB
  1627.  
  1628.  
  1629. Because the actual value of the argument is being passed, there is no way
  1630. to return information back to the caller.  But in those situations where
  1631. an assignment to the original variable from within the routine is not
  1632. needed, BYVAL can eliminate a lot of compiler-generated code when dealing
  1633. with integers.  Of course, you may use a mix of BYVAL and non-BYVAL
  1634. parameters if you need the benefits of both methods in a single call.
  1635.    As proof of this savings, disassemblies of a one-statement subprogram
  1636. designed both ways is presented below, to show how an integer parameter is
  1637. accessed when it is passed by address and by value.
  1638.  
  1639.  
  1640. SUB ByAddress(Param%) STATIC
  1641. LocVar% = Param%
  1642.   MOV  SI,[Param%]   ;get the address of Param%
  1643.   MOV  AX,[SI]       ;then read the value there
  1644.   MOV  LocVar%,AX    ;assign that to LocVar%
  1645. END SUB
  1646.  
  1647.  
  1648. SUB ByValue(BYVAL Param%) STATIC
  1649. LocVar% = Param%
  1650.   MOV  AX,Param%     ;read Param% directly
  1651.   MOV  LocVar%,AX    ;and assign it to LocVar%
  1652. END SUB
  1653.  
  1654.  
  1655. Note that the savings are only within the subroutine, and not when it is
  1656. called.  That is, 4 bytes are needed to pass an integer variable whether
  1657. by address or by value.  In fact, passing larger data types requires more
  1658. code to pass by value.  Any variable can be passed by address with 4 bytes
  1659. of compiler-generated code, because what is sent is a single address.  But
  1660. to pass a double precision number by value requires 16 bytes, since 4 bytes
  1661. of code are needed for each 2-byte portion of the number.
  1662.    In general, passing variables as parameters to a subprogram or function
  1663. is preferable to sharing them.  When many variables are shared throughout
  1664. a program, you run the risk of introducing bugs caused by accidentally
  1665. using the same variable name more than once.  However, sharing has some
  1666. definite advantages in at least two situations.
  1667.    The first is when a procedure must be accessed as quickly as possible. 
  1668. Since a finite amount of code is needed to pass each parameter, some amount
  1669. of time is also required to execute that code.  Therefore, sharing a few,
  1670. carefully selected variables can improve the speed of your programs and
  1671. reduce their size as well.  Another important use for SHARED is to conserve
  1672. data memory.  Nearly all programs use at least a few temporary scratch
  1673. variables, perhaps as FOR/NEXT loop counters.  By dimensioning several such
  1674. variables as being shared throughout a program, the same variables can be
  1675. used repeatedly.  I often begin programs with a DIM SHARED statement such
  1676. as DIM SHARED X, Y, Z, and then use those variables as often as possible.
  1677.    One final trick I want to share is how to pass a large number of
  1678. parameters using less code than would normally be necessary.  Each argument
  1679. that is passed to a procedure requires 4 bytes of code.  In a complicated
  1680. routine that needs many parameters, this can quickly add up.  Worse, these
  1681. bytes are added for every call.  Therefore, a subprogram that accepts 10
  1682. parameters and is called 20 times will add 800 bytes to the final
  1683. executable file just to handle the parameters!
  1684.    One solution is to use an array, which is ideal when all of the
  1685. parameters are the same type of data.  An entire array can be passed as a
  1686. single parameter since only the array descriptor's address is needed.  Even
  1687. better, however, is to create a TYPE variable, and then assign all of the
  1688. parameters to it.  A TYPE variable can hold nearly any amount and type of
  1689. data, and it too can be passed using only 4 bytes.  Although this does
  1690. require a separate assignment for each TYPE component, you simply use the
  1691. TYPE where the regular variables would have been assigned.  By eliminating
  1692. the added code to pass many parameters, programs that use a TYPE this way
  1693. will also be much faster.
  1694.  
  1695.  
  1696. MODULAR PROGRAMMING
  1697.  
  1698. QuickBASIC versions 4.0 and later let you load subprograms and functions
  1699. from multiple files into the editing environment at the same time.  This
  1700. further enhances their reusability, since the different modules can be
  1701. treated as "black boxes" whose purpose is already known.  Once a routine
  1702. has been developed and debugged, it can be used again and again, without
  1703. further regard for the names of the variables within the routines.  Indeed,
  1704. many of the utility routines included with this book are provided as
  1705. separate modules, intended to be loaded along with your programs.
  1706.    Any variable name can be passed as an argument to a procedure, even if
  1707. a different name is used to represent the same variable within the
  1708. procedure.  If you have defined a subprogram such as SUB MySub(X%, Y!, Z$),
  1709. then you could call it using CALL MySub(A%, B!, C$).  Of course, the
  1710. variables you pass must be of the same data type as the subroutine expects.
  1711.    Because reusability is an important consideration in the design of any
  1712. procedure, it generally makes sense to store it in its own source file. 
  1713. This lets you combine the same module repeatedly with any number of
  1714. programs.  The alternative would be to merge the file into each program
  1715. that needs it.  But maintaining multiple copies of the same code wastes
  1716. disk space.  Further, if a bug is found in the routine, you will have to
  1717. identify all of the programs that contain it, and manually correct each
  1718. one of them.
  1719.    Another important advantage of using separate files is that you can
  1720. exceed the usual 64K code size barrier.  Unlike the data segment which is
  1721. comprised of the sum of all data in all modules, an .EXE file can contain
  1722. multiple code segments.  Each BASIC module has a single code segment, and
  1723. each of these can be as large as 64K.  In fact, dividing a program into
  1724. separate files is the *only* way to exceed the usual 64K code size
  1725. limitation.
  1726.    Although using a separate source file for each subprogram makes sense
  1727. in many situations, there is one slight disadvantage.  When all of the
  1728. various program modules are linked together, each separate module adds
  1729. approximately 100 bytes of overhead.  None the less, for all but the
  1730. smallest programming projects, the advantages of using separate modules
  1731. will probably outweigh the slight increase in code size.
  1732.  
  1733.  
  1734. INCLUDE FILES
  1735.  
  1736. Another useful BASIC feature that can help you to create modular programs
  1737. is the Include file.  An Include file is a separate file that is read and
  1738. processed by BASIC at a specified place in your program.  The statement
  1739. '$INCLUDE: 'filename' tells QB or BC to add the statements in the named
  1740. file to your source code, as if that code had been entered manually.  If
  1741. a file extension is not given, then .BAS is assumed.  Many of the files
  1742. that Microsoft provides with QuickBASIC use a .BI extension, which stands
  1743. for "BASIC Include".  Some programmers use .INC, and you may use whatever
  1744. seems appropriate to the contents of the file.
  1745.    Include files are ideal for storing DECLARE, CONST, TYPE, and COMMON
  1746. statements.  Except for COMMON, none of these statements add to the size
  1747. of your program, and none of them create any executable code.  Therefore,
  1748. you could create a single include file that is used for an entire project,
  1749. and add an appropriate '$INCLUDE directive to the beginning of each program
  1750. source file.  Unused DECLARE and CONST statements and TYPE definitions are
  1751. ignored by BASIC if they are not referenced.  However, they do impinge
  1752. slightly on available memory within the QuickBASIC editor, since BASIC has
  1753. no way to know that they are not being used.  Similarly, BC must keep track
  1754. of the information in these statements as it compiles your program.  But
  1755. again, there is no impact on the size of your final executable program.
  1756.    In general, I recommend that you avoid placing any executable statements
  1757. into an include file.  Because the code in an include file is normally
  1758. hidden from your view, it is easy to miss a key statement that is causing
  1759. a bug.  Likewise, a '$DYNAMIC or '$STATIC command hidden within an include
  1760. file will obscure the true type of any arrays that are subsequently
  1761. dimensioned.  Perhaps worst of all is placing a DEFINT or other DEFtype
  1762. statement there, for the same reason.
  1763.  
  1764.  
  1765. QUICK LIBRARIES
  1766.  
  1767. Quick Libraries contribute to modular programming in two important ways. 
  1768. Perhaps the most important use for a Quick Library is to allow access to
  1769. subprograms and functions that are not written in BASIC.  All DOS programs
  1770. and subroutines--regardless of the language they were originally written
  1771. in--end up as .OBJ files suitable for LINK to join together.  But the QB
  1772. and QBX editing environments manipulate BASIC source code, and interpret
  1773. the commands rather than truly compile them.  Therefore, the only way you
  1774. can access a routine written in assembly language or C within QuickBASIC
  1775. is by placing the routine into a Quick Library.
  1776.    Quick Libraries also let you store completed BASIC subprograms and
  1777. functions out of the way from the rest of your program.  If you have a
  1778. large number of subroutines in one program, the list of names displayed
  1779. when F2 is pressed can be very long and confusing.  Since QuickBASIC does
  1780. not display the routines in a Quick Library, there will be that many fewer
  1781. names to deal with.  Another advantage of placing pre-compiled BASIC
  1782. routines into a Quick Library is that they can take less memory than when
  1783. the BASIC source code is loaded as a module.  This is true especially when
  1784. you have many comments in the program, since comments are of course not
  1785. compiled.
  1786.    Be aware that there are a few disadvantages to placing BASIC code into
  1787. a Quick Library.  One is that you cannot step and trace through the code,
  1788. since it is not in its original BASIC source form.  Another is that Quick
  1789. Libraries are always stored in normal DOS memory, as opposed to expanded
  1790. memory which QBX [and VB/DOS] can use.  When a BASIC subprogram or function
  1791. is less than 16K in size and EMS is present, QBX [and VB/DOS] will place
  1792. its source code in expanded memory to free up as much conventional memory
  1793. as possible.
  1794.  
  1795.  
  1796. ERROR AND EVENT HANDLING
  1797. ========================
  1798.  
  1799. As a BASIC programmer, there are several types of errors that you must deal
  1800. with in a program.  These errors fall into two general categories: compile
  1801. errors and runtime errors.  Compile errors are those that QB or BC issue,
  1802. such as "Syntax error" or "Include file not found".  Generally, these are
  1803. easy to understand and correct, because the QuickBASIC editor places the
  1804. cursor beneath the offending statement.  In some cases, however, the error
  1805. that is reported is incorrect.  For example, if your program uses a
  1806. function in a Quick Library that expects a string parameter and you forgot
  1807. to declare it, BASIC reports a "Type mismatch" error.  After all, with a
  1808. statement such as X = FuncName%(Some$), how could BASIC know that FuncName%
  1809. is not simply an integer array?  Assuming that it is an array, BASIC
  1810. rejects Some$ as being illegal for an element number.
  1811.    Runtime errors are those such as "File not found" which are issued when
  1812. your program tries to open a file that doesn't exist, or is not in the
  1813. specified directory.  Other common runtime errors are "Illegal function
  1814. call", "Out of string space", and "Input past end".  Many of these errors
  1815. can be avoided by an explicit test.  If you are concerned that string space
  1816. might be limited you can query the FRE("") function before dimensioning a
  1817. dynamic string array.  However, some errors are more difficult to
  1818. anticipate.  For example, to determine if a particular directory exists you
  1819. must use CALL Interrupt to query a DOS service.
  1820.    The conventional way to handle errors is to use ON ERROR, and design an
  1821. error handling subroutine.  There are a number of problems with using ON
  1822. ERROR, and most professional programmers try to avoid using it whenever
  1823. possible.  But ON ERROR does work, and it is often the simplest and most
  1824. direct solution in many programs.  The short listing below shows the
  1825. minimum steps necessary to implement an error handler using ON ERROR.
  1826.  
  1827.  
  1828. ON ERROR GOTO HandleErr
  1829. FILES "*.XYZ"
  1830. END
  1831.  
  1832. HandleErr:
  1833. SELECT CASE ERR
  1834.   CASE 53: PRINT "File not found"
  1835.   CASE 68: PRINT "Device unavailable"
  1836.   CASE 71: PRINT "Disk not ready"
  1837.   CASE 76: PRINT "Path not found"
  1838.   CASE ELSE: PRINT "Error number"; ERR
  1839. END SELECT
  1840. RESUME NEXT
  1841.  
  1842.  
  1843. The statement ON ERROR GOTO HandleErr tells BASIC that if an error occurs,
  1844. the program should jump to the HandleErr label.  Without ON ERROR, the
  1845. program would display an error message and then end.  Since it is unlikely
  1846. that you have any files with an .XYZ extension, BASIC will go to the error
  1847. handler when this program is run.  Within the error handling routine, the
  1848. program uses the ERR function to determine the number of the error that
  1849. occurred.  Had line numbers been used in the program, the line number in
  1850. which the error occurred would also be available with the ERL function.
  1851.    In this brief program fragment, the most likely error numbers are
  1852. filtered through a SELECT CASE block, and any others will be reported by
  1853. number.  Regardless of which error occurred, a RESUME NEXT statement is
  1854. used to resume execution at the next program statement.  RESUME can also
  1855. be used with an explicit line label or number to resume there; if no
  1856. argument is given BASIC resumes execution at the line that caused the
  1857. error.  In many cases a plain RESUME will cause the program to enter an
  1858. endless loop, because the error will keep happening repeatedly.
  1859.    In this case, the file will not exist no matter how many times BASIC
  1860. tries to find it.  Therefore, a plain RESUME is not appropriate following
  1861. a "File not found" or similar error.  Had the error been "Disk not ready",
  1862. you could prompt the user to check the drive and then press a key to try
  1863. again.  In that case, then, RESUME would make sense.  Although BASIC's ON
  1864. ERROR can be useful, it does have a number of inherent limitations.
  1865.    Perhaps the worst problem with ON ERROR is that it often increases the
  1866. program's size.  When you use RESUME NEXT, you must also use the /x compile
  1867. switch.  Unfortunately, /x adds internal address labels to show where each
  1868. statement begins, so the RESUME statement can find the line that caused the
  1869. error.  These labels are included within the compiled code and therefore
  1870. increases its size.
  1871.    Another problem with ON ERROR is that it can hide what is really
  1872. happening in a program.  I recommend strongly that you REM out all ON ERROR
  1873. statements while working in the QuickBASIC editing environment.  Otherwise,
  1874. an Illegal function call or other error may cause QuickBASIC to go to your
  1875. error handler, and that handler might ignore it if the error is not one you
  1876. were expecting and testing for.  If that happens and your program uses
  1877. RESUME NEXT, you might never even know that an error occurred!
  1878.    Yet another problem with ON ERROR is that it's frankly a clumsy way to
  1879. program.  Most languages let you test for the success or failure of the
  1880. most recent operation, and act on or ignore the results at your discretion. 
  1881. Pascal, for example, uses the IOResult function to indicate if an error
  1882. occurred during the last input or output operation.
  1883.    Finally, BASIC generates errors for many otherwise proper circumstances,
  1884. such as the FILES statement above.  You might think that if no files were
  1885. found that matched the .XYZ extension given, then BASIC would simply not
  1886. display anything.  Indeed, an important part of toolbox products such as
  1887. Crescent Software's QuickPak Professional are the routines that replace
  1888. BASIC's file handling statements.  By providing replacement routines that
  1889. let you test for errors without an explicit ON ERROR statement, an add-on
  1890. library can help to improve the organization of your programs.
  1891.    As I mentioned earlier, some errors can be avoided by using CALL
  1892. Interrupt to access DOS directly.  (One important DOS service lets you see
  1893. if a file exists before attempting to open it.)  But critical errors such
  1894. as those caused by an open drive door require assembly language.  In
  1895. Chapter 12 you will learn how to bypass BASIC and access DOS directly using
  1896. CALL Interrupt.
  1897.  
  1898.  
  1899. EVENT HANDLING
  1900.  
  1901. BASIC includes several forms of event handling, and like ON ERROR, these
  1902. too are avoided when possible by many professional programmers.  Event
  1903. handling lets your programs perform a GOSUB automatically and without any
  1904. action on your part, based on one or more conditions.  Some of the more
  1905. commonly used event statements are ON KEY, ON TIMER, and ON COM.  With ON
  1906. KEY, you can specify that a particular key or combination of keys will
  1907. temporarily halt the program, and branch to a GOSUB routine designated as
  1908. the ON KEY handler.  ON TIMER is similar, except it performs a GOSUB at
  1909. regular intervals based on BASIC's TIMER function.  Likewise, ON COM
  1910. performs a GOSUB whenever a character is received at the specified
  1911. communications port.
  1912.    The concept of event handling is very powerful indeed.  For example, ON
  1913. COM allows your program to go about its business, and also handle
  1914. characters as they arrive at the communications port. ON TIMER lets you
  1915. simulate a crude form of multi-tasking, where control is transferred to a
  1916. separate subroutine at one second intervals.  Unfortunately, BASIC's event
  1917. handling is not truly interrupt driven, and the resulting code to implement
  1918. it adds considerably to a program's size.
  1919.    When any of the event handling methods are used, BASIC calls an interval
  1920. event dispatcher periodically in your program.  These calls add five bytes
  1921. apiece, and one is added at either every statement, or at every labeled
  1922. statement [depending on whether you compiled using /v or /w respectively]. 
  1923. This can increase your program's size considerably.  Even worse, the
  1924. repeated calls have an adverse effect on the speed of most programs.  Like
  1925. ON ERROR, BASIC's event handling statements provide a simple solution that
  1926. is effective in many programming situations.  And also like ON ERROR, they
  1927. are best avoided in important programming projects.
  1928.    Using purely BASIC techniques, the only alternative to event trapping
  1929. is polling.  Polling simply means that your program manually checks for
  1930. events, instead of letting BASIC do it automatically.  The primary
  1931. advantage of polling is that you can control when and where this checking
  1932. occurs.  The disadvantage is that it requires more effort by you.
  1933.    To see if any characters have been received from a communications port
  1934. but are still waiting to be read you would use the LOF function.  And to
  1935. see if a given amount of time has elapsed you must query the TIMER function
  1936. periodically.  If true interrupt driven event handling were available in
  1937. BASIC, that would clearly be preferable to either of the two available
  1938. methods.  However, only with Crescent's P.D.Q. product can such capability
  1939. be added to a BASIC program.
  1940.  
  1941.  
  1942. PROGRAMMING STYLE
  1943.  
  1944. Programming style is a personal issue, and every programmer develops his
  1945. or her own particular methods over time.  Some aspects of programming style
  1946. have little or no impact on the quality of the final result.  For example,
  1947. the number of columns you indent a FOR/NEXT loop will not affect how
  1948. quickly a sort routine operates.  But there are style factors that can help
  1949. or harm your programs.  One is that clearly commenting your code will help
  1950. you to understand and improve it later.  Another is when more than one
  1951. programmer is working on a large project simultaneously.  If neither
  1952. programmer can figure out what the other is doing, the program's quality
  1953. will no doubt suffer.
  1954.    Clearly, no one can or even should try to force a particular style or
  1955. ideology upon you.  However, I would like to share some of the decisions
  1956. that I have made over the years, and explain why they make sense to me. 
  1957. Of course, you are free to use or not use these opinions as you see fit. 
  1958. Programmers are as unique and varied as any other discipline, and no one
  1959. set of rules could possibly serve everyone equally.  Whatever conventions
  1960. you settle upon, be consistent above all else.
  1961.    The most important convention that I follow is to use DEFINT A-Z as the
  1962. first statement in every program.  For me, using integers verges on
  1963. religion, and my fingers could type DEFINT even if I were asleep.  As I
  1964. have stated repeatedly, integers should be used whenever possible, unless
  1965. you have a compelling reason not to.  Integers are much faster and smaller
  1966. than any other variable type BASIC offers.  Nearly all of the available
  1967. third party add-on products use integers parameters wherever possible, and
  1968. so should the routines you write.  The only reasonable exception to this
  1969. is when writing financial or scientific programs, or other math-intensive
  1970. applications.
  1971.    Equally important is adding sufficient and appropriate comments.  Some
  1972. programmers like to use comment headers that identify each related block
  1973. of code; others prefer to comment every line.  I recommend doing both,
  1974. especially if other people will be reading your programs.  I also prefer
  1975. using an apostrophe as a comment delimiter, rather than the more formal
  1976. REM.  There are only so many columns available for each comment line, and
  1977. it seems a shame to waste the space REM requires.
  1978.    When writing a subprogram or function that you plan to use again in
  1979. other projects, include a complete heading comment that shows the purpose
  1980. of the routine and the parameters it expects.  If each parameter is listed
  1981. neatly at the beginning of the file, you can create a hardcopy index of
  1982. routines by printing that section of each file.
  1983.    Avoid comments that are obvious or redundant, such as this:
  1984.  
  1985.    Count = Count + 1 'increment Count
  1986.  
  1987. If Count is keeping track of the number of lines read from a file, a more
  1988. appropriate comment would be 'show that another line was read.  Also avoid
  1989. comments that are too cute or flip.  Simply state clearly what is happening
  1990. so you will know what you had in mind when you come back to the program
  1991. next month or next year.
  1992.    Selecting meaningful variable names is equally valuable in the overall
  1993. design of a program.  If you are keeping track of the current line in a
  1994. file, use a variable name such as CurLine.  Although BASIC in some cases
  1995. lets you use a reserved word as a variable name, I recommend against that. 
  1996. Over the years, different versions of BASIC have allowed or disallowed
  1997. different keywords for variables.  While QuickBASIC 4.5 lets you use Name$
  1998. as a variable, there is no guarantee that the next version will.  Also, be
  1999. aware that variables names which begin with the letters Fn are illegal,
  2000. because BASIC reserves that for user-defined functions.  Using the variable
  2001. FName$ to hold a file name may look legal, but it isn't.
  2002.    Don't be ashamed to use GOTO when it is appropriate.  There are many
  2003. places where GOTO is the most direct way to accomplish something.  As I
  2004. showed earlier in this chapter, GOTO when used correctly can sometimes
  2005. produce smaller and faster code than any other method.
  2006.    Use line labels instead of line numbers.  The statement GOSUB 1020
  2007. doesn't provide any indication as to what happens at line 1020.  GOSUB
  2008. OpenFile, on the other hand, reads like plain English.  The only exception
  2009. to this is when you are debugging a program that crashes with the message
  2010. "Illegal function call at line no line number".  In that case, you should
  2011. *add* line numbers to your program and run it again.  A program that reads
  2012. a source file and prints each line to another file with sequential numbers
  2013. is trivial to write.  I will also discuss debugging in depth in Chapter 4.
  2014.    Even though using DEFINT is supposed to force all subsequent CONST, DEF
  2015. FN, and FUNCTION declarations to be integer, a bug in QuickBASIC causes
  2016. untyped names to occasionally assume the single precision default. 
  2017. Therefore, I always use an explicit percent sign (%) to establish each
  2018. function's type.  In fact, I use whatever type identifier is appropriate
  2019. for functions and CONST statements, to make them easily distinguishable in
  2020. the program listing.  For example, in the statement IF CurRow > MaxRows%
  2021. THEN CurRow = MaxRows%, I know that MaxRows% has been defined as a
  2022. constant.  Some people prefer to use all upper-case letters for constants,
  2023. though I prefer to reserve upper case for BASIC keywords.
  2024.    Although BASIC supports the optional AS INTEGER and AS SINGLE directives
  2025. when defining a subprogram or function, that wastes a lot of screen space. 
  2026. I greatly prefer using the variable type identifiers.  That is, I will use
  2027. SUB MySub(A%, B!) rather than SUB MySub(A AS INTEGER, B AS SINGLE).  The
  2028. same information is conveyed but with a lot less effort and screen clutter.
  2029.    A well-behaved subroutine will restore the PC to the state it was when
  2030. called.  If you have subprogram that prints a string centered on the bottom
  2031. line of the screen, use CSRLIN and POS(0) to read the current cursor
  2032. location before you change it.  Then restore the cursor before you exit.
  2033.    I like to indent two spaces within FOR/NEXT and IF/THEN blocks. 
  2034. Although some people prefer indenting four or even eight columns for each
  2035. level, that can quickly get out of hand when the blocks are deeply nested. 
  2036. Nothing is harder to read than code that extends beyond the edge of the
  2037. screen.  But whatever you do, please *do not* change the tab stop settings
  2038. in the QuickBASIC editor, unless you are the only one who will ever have
  2039. to look at your code.  Even though the program may look fine on your
  2040. screen, the indentation will be completely wrong on everyone else's PC.
  2041.    When creating a dynamic array I prefer REDIM to a previous '$DYNAMIC
  2042. statement.  REDIM is clearer because it shows at the point in the source
  2043. where the array is dimensioned that this is a dynamic array.  Otherwise you
  2044. have to scan backwards through your source code looking for the most recent
  2045. '$DYNAMIC or '$STATIC, to see what type of array it really is.  By the same
  2046. token, using ever-changing DEFtype statements throughout your code is poor
  2047. practice.  Further, if a variable is a string, always include the dollar
  2048. sign ($) suffix when you reference it.  If you use DEFSTR S or even worse,
  2049. DIM xxx AS STRING and then omit the dollar sign, nobody else will
  2050. understand your program.
  2051.    I also prefer to explicitly dimension all arrays, and not let BC create
  2052. them with the 11-element default (including element zero).  If you need
  2053. less than 11 elements, the memory is wasted.  And if you need more, then
  2054. your program will behave unpredictably.  Not dimensioning every array is
  2055. sloppy programming.  Period.
  2056.    Avoid repeated calls to BASIC's internal functions if possible.  In the
  2057. listing below, the first example creates 61 bytes of code, while the second
  2058. generates only 46 bytes.
  2059.  
  2060.  
  2061. Not recommended:  
  2062.  
  2063. IF CSRLIN = 1 OR CSRLIN = 6 OR CSRLIN = 12 THEN
  2064.   ...
  2065. END IF
  2066.  
  2067.  
  2068. Much better:
  2069.  
  2070. Temp = CSRLIN
  2071. IF Temp = 1 OR Temp = 6 OR Temp = 12 THEN
  2072.   ...
  2073. END IF
  2074.  
  2075.  
  2076. As I stated earlier in this chapter, using SELECT CASE instead of IF will
  2077. also eliminate this problem.  Many BASIC statements are translated into
  2078. calls, and each call takes a minimum of five bytes.
  2079.    Your programs will be easier to read if you evaluate temporary
  2080. expressions separately.  Even though BASIC lets you nest parentheses to
  2081. nearly any level, nothing is gained by packing many expressions into a
  2082. single statement.  In the examples below that strip the extension from a
  2083. file name, the first creates only a few bytes less code.  Although this may
  2084. seem counter to the other advice I have given, a slight code increase is
  2085. often more than offset by a commensurate improvement in clarity.
  2086.  
  2087.  
  2088. File$ = LEFT$(File$, INSTR(File$, ".") - 1)
  2089.  
  2090.  
  2091. Dot = INSTR(File$, ".")
  2092. File$ = LEFT$(File$, Dot - 1)
  2093.  
  2094.  
  2095. The last issue I want to discuss is how to pronounce BASIC keywords and
  2096. variable names.  Don't laugh, but many programmers have no idea how to
  2097. communicate the words LEFT$ or VARSEG over the telephone.  Some people say
  2098. "X dollar" for X$ even though "X string" is so much easier to say.  Another
  2099. keyword that's hard to verbalize is VARPTR.  I prefer "var pointer" since
  2100. it is, after all, a pointer function.  CHR$(13) is pronounced "character
  2101. string thirteen", again because that's the clearest and most straight
  2102. forward interpretation.  Likewise, INSTR is pronounced "in string" and
  2103. LEFT$ would be said as "left string".  If you're not sure how to pronounce
  2104. something, use the closest equivalent English wording you can think of.
  2105.  
  2106.  
  2107. SUMMARY
  2108.  
  2109. In this chapter you have learned how BASIC's control flow statements are
  2110. constructed, and how the compiler-generated code is similar regardless of
  2111. which statements are used.  You also learned where GOSUB and GOTO should
  2112. be used, and when subprograms and functions are more appropriate.  The
  2113. discussion on logical operations showed how AND, OR, EQV, and XOR operate,
  2114. and how they can be used to advantage in your programs.
  2115.    I have explained in detail exactly what recursion is, and how recursive
  2116. subroutines can perform services that are not possible using any other
  2117. technique.  You have also learned about the importance of the stack in
  2118. recursive and other non-static subroutines.  Passing parameters to
  2119. subprograms and functions has also been described in detail, along with
  2120. some of the principles of modular program and event handling.
  2121.    Finally, I have shared with you some of my own personal preferences
  2122. regarding programming style, and when and how such conventions can make a
  2123. difference.  Although this is a personal issue, I firmly believe it is
  2124. important to develop a consistent style and stick with it.
  2125.    In Chapter 4 you will learn debugging methods using both the QuickBASIC
  2126. editing environment and Microsoft's CodeView debugger.  The successful
  2127. design of a program is but one part of its development.  Once it has been
  2128. written, it must also be made to work correctly and reliably.  As you will
  2129. learn, there are many techniques that can be used to identify and correct
  2130. common programming errors.
  2131.